DynamoDBMappingException when using Java Generics - java

I have a DynamoDB table called 'inbox'. In it will be stored messages that are sent to and from users. There will be more than one type of message, and based on the type, data may come along with the message. I have an object UserMessage that maps to the inbox table. While there will be common fields (e.g. sendTo, sentFrom) for all messages, the data will vary in its structure.
Rather than using a Map for this data, the thought occurs that using Java Generics might be a better approach. I have an object annotated with #DynamoDBDocument, and DynamoDBMapper will serialize that to JSON. When the member variable is declared as:
private ContactData data;
indeed, the result is expected. data is serialized to JSON and stored in the data attribute in inbox in the format of ContactData's annotations. However, to get the flexibility desired by using generic types, a call to DynamoDBMapper.save() throws DynamoDBMappingException: Cannot convert T to a class.
Here is the UserMessage class:
#DynamoDBTable(tableName="inbox")
public class UserMessage<T> {
private String toId;
private T data;
#DynamoDBAttribute(attributeName="data")
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
#DynamoDBHashKey(attributeName="toId")
public String getToId() {
return toId;
}
public void setToId(String to) {
this.toId = to;
}
}
And the code for ContactData:
#DynamoDBDocument
public class ContactData {
private String firstname;
private String lastname;
private String email;
#DynamoDBAttribute(attributeName = "firstname")
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
#DynamoDBAttribute(attributeName = "lastname")
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
#DynamoDBAttribute(attributeName = "email")
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
The controller code to set this up is:
UserMessage<ContactData> message = new UserMessage<ContactData>();
ContactData cd = new ContactData();
cd.setEmail("jane#test.com");
cd.setFirstname("Jane");
cd.setLastname("Smith");
message.setToId("test#email.com");
message.setData(cd)
DynamoDB.getMapper().save(message);
I'm fairly inexperienced, and generic types are brand new to me, so I hope I am using them correctly. I think I am. I just think that DynamoDBMapper can't map to the right class in this case.
Thanks
Jeff

Everytime you use a non-standard attribute class you need to provider a marshaller. Here I used a global Object marshaller you can probably use to just JSON-ify anything you'd like.
I personally prefer using static types, each one with a defined marshaller I can reason about, but ymmv.
public class GenericDBEntity<T>
{
..
private T extra;
#DynamoDBAttribute
#DynamoDBMarshalling(marshallerClass=ObjectMarshaller.class)
public T getExtra()
{
return extra;
}
public void setExtra(T extra)
{
this.extra = extra;
}
public static class ObjectMarshaller implements DynamoDBMarshaller<Object>
{
#Override
public String marshall(Object getterReturnResult)
{
return getterReturnResult.toString();
}
#Override
public Object unmarshall(Class<Object> clazz, String obj)
{
return obj;
}
}
}

Related

How to implement the Builder Design Pattern in the right way?

I have found different implementations of the Builder pattern when learning about design patterns. Some implementations use an interface/abstract-class to represent the builder, others use just an static class.
Which one is the right way to implement the Builder Design Pattern?
Below, an implementation using an abstract class (ComputerBuilder) (Source)
public class LaptopBuilder : ComputerBuilder
{
Computer computer;
public LaptopBuilder()
{
computer = new Computer("Laptop");
}
public override void BuildOS()
{
//TODO
}
public override void BuildDevice()
{
//TODO
}
public Computer ComputerType
{
get { return computer; }
}
}
public class DesktopBuilder : ComputerBuilder
{
Computer computer;
public DesktopBuilder()
{
computer = new Computer("Desktop");
}
public override void BuildOS()
{
//TODO
}
public override void BuildDevice()
{
//TODO
}
public Computer ComputerType
{
get { return computer; }
}
}
Below, another implementation, Neither using abstract class nor interface, but an static class instead. (Source)
public class User
{
//All final attributes
private final String firstName; // required
private final String lastName; // required
private final int age; // optional
private final String phone; // optional
private final String address; // optional
private User(UserBuilder builder) {
this.firstName = builder.firstName;
this.lastName = builder.lastName;
this.age = builder.age;
this.phone = builder.phone;
this.address = builder.address;
}
//All getter, and NO setter to provde immutability
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public int getAge() {
return age;
}
public String getPhone() {
return phone;
}
public String getAddress() {
return address;
}
#Override
public String toString() {
return "User: "+this.firstName+", "+this.lastName+", "+this.age+", "+this.phone+", "+this.address;
}
public static class UserBuilder
{
private final String firstName;
private final String lastName;
private int age;
private String phone;
private String address;
public UserBuilder(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public UserBuilder age(int age) {
this.age = age;
return this;
}
public UserBuilder phone(String phone) {
this.phone = phone;
return this;
}
public UserBuilder address(String address) {
this.address = address;
return this;
}
//Return the finally consrcuted User object
public User build() {
User user = new User(this);
validateUserObject(user);
return user;
}
private void validateUserObject(User user) {
//Do some basic validations to check
//if user object does not break any assumption of system
}
}
}
If you actually read the second article you may notice that it fairly early on states that
"I want to make it clear that the builder pattern which I am going to discuss in this post, is slightly different from what is mentioned in GangOfFour “Design Patterns” book." (Author's emphasis)
A little later, he or she writes:
"For me, a builder pattern is more like fluent interface."
Notice the little qualifier, for me.
This should tell you that what you see isn't the 'canonical' representation of the pattern, but a variation.
That said, these representations are variations of the same underlying idea.
It's okay to look at alternative ways to express a concept. We should be careful that we don't elevate the Gang of Four book to unassailable status.
For me (pun intended) Builder is the best example of a GoF pattern that has been improved since the book was published. I believe it was Josh Bloch's version from Effective Java that popularized the static approach; but there are versions originating from blog posts that are more useful and/or less complicated than the GoF version.
As often as I refer back to the GoF book, I never reread the Builder chapter, because better alternatives are available. Of course, you have to decide which alternative is better for you.

Firestore: (21.4.1) [CustomClassMapper]: No setter/field

I have a User class which saves some extra data on the user. This data is stored in/coming from Firestore. I have a couple of fields which are working(name, surname, lastLogin) but a couple of them are not working(blocked).
When I make the field public they work, but when I try to use a setter, it doesn't. I tried cleaning the build and rebuilding it. I know it is not saving the field due to #Exclude, that is intended.
What am I doing wrong? The field type doesn't matter, I've added a new String field which gave the same warning, while name and surname work.
The database:
**userid**
{
"name" : "John",
"surname" : "Doe",
"lastLogin" : **timestamp**,
"blocked" : true
}
The class:
#Keep
public class User
{
private String name;
private String surname;
private Date lastLogin;
private boolean blocked = false;
public User()
{
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public String getSurname()
{
return surname;
}
public void setSurname(String surname)
{
this.surname = surname;
}
public Date getLastLogin()
{
return lastLogin;
}
public void setLastLogin(Date lastLogin)
{
this.lastLogin = lastLogin;
}
#Exclude
public boolean isBlocked()
{
return blocked;
}
public void setBlocked(boolean blocked)
{
this.blocked = blocked;
}
The problem in your code is that the constructor in the User class is private. That's not the correct way in which you should create a new instance of the class. JavaBeans require a no-argument constructor to be present.
When Cloud Firestore SDK deserializes objects that are coming from the database, it requires that any objects in use, to have this public no-argument constructor, so it can use it to instantiate the object. Fields in the objects are set by using public setter methods or direct access to public members, as you already tried.
Because your constructor is private, the SDK doesn't really know how to create an instance of it. So it is mandatory to change it as public. A correct way to create that class should be:
class User {
private String name;
private String surname;
private long lastLogin;
private boolean blocked = false;
public User() {} //Needed for Cloud Firestore
public User(String name, String surname, long lastLogin, boolean blocked) {
this.name = name;
this.surname = surname;
this.lastLogin = lastLogin;
this.blocked = blocked;
}
//Getters and setters are not mandatory
}
Also please note that the setters and the getters are not required. Setters are always optional because if there is no setter for a JSON property, the Firebase client will set the value directly onto the field.
Edit:
According to your comment:
but it does not explain why some fields are working and others aren't. It should not work at all, right?
Yes, that's right, all should work. The reason why some of them are not working is that the blocked property in your User class is of type boolean while in your database is of type String and this is not correct. Both types must match.
And the private constructor is due to the singleton instance, as far as I know, the constructor should be private to avoid creating new instances of the class.
No, the constructor must be public. I think there is a misunderstanding. Every time you use FirebaseDatabase.getInstance(), a single socket connection between your application and the Firebase servers is opened. From that moment on, all traffic between the application and the database goes over the same socket. So it doesn't matter how many times you create an instance, it will always be a single connection. Regarding your POJO class, there is no need for such a Singleton because Firebase always needs to know how to create an instance of that class, using the public no-argument constructor.
Try to create a constructor with parameters for all class attributes along with a non-parameter constructor and then in the java class where you store in firebase, create object from user and pass it.
for example:
package com.example.spacing.Model;
public class User {
private String username;
private String phone;
private String id;
private String imageURL;
private String email;
public User(String username, String email ,String phone, String id, String imageURL) {
this.username = username;
this.email=email;
this.phone = phone;
this.id = id;
this.imageURL = imageURL;
}
public String getImageURL() {
return imageURL;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public User() {
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
}
and
FirebaseDatabase.getInstance().getReference("Users")
.child(FirebaseAuth.getInstance().getCurrentUser().getUid())
.setValue(user);
You can try to add #field:JvmField to your boolean variable inside your User class.

Saving imported objects to DynamoDB in a java application

I am trying to save an object into DynamoDB. I know that the easiest way to do this is to annotate the object with #DynamoDBDocument.
However, in my case, the objects I want to save belong to a package that I can't modify.
I am using the java sdk.
import not.my.package.Outsider;
#DynamoDBTable(tableName = "DynamoTable")
public class DynamoTable {
private Outsider outsider;
//getters...
//setters...
}
Any ideas on how I can save these objects? I do not want to save them as a string as we are using a Dynamo to SQL plugin for our business purposes.
Thanks.
Firstly, the OP doesn't have any information about partition key and sort key. The below code auto generates the partition key using annotation #DynamoDBAutoGeneratedKey. You can change it based on your use case.
Order class - Similar to DynamoTable
#DynamoDBTable(tableName = "Order")
public class Order implements Serializable {
private static final long serialVersionUID = -3534650012619938612L;
private String orderId;
private String productName;
private Integer createDate;
private Outsider outsider;
#DynamoDBHashKey(attributeName = "orderId")
#DynamoDBAutoGeneratedKey
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
#DynamoDBAttribute(attributeName = "productName")
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
#DynamoDBAttribute(attributeName = "createDate")
public Integer getCreateDate() {
return createDate;
}
public void setCreateDate(Integer createDate) {
this.createDate = createDate;
}
#DynamoDBAttribute(attributeName = "outsider")
public Outsider getOutsider() {
return outsider;
}
public void setOutsider(Outsider outsider) {
this.outsider = outsider;
}
}
Outsider class:-
The attributes in outsider class will be saved as Map attribute in DynamoDB table.
#DynamoDBDocument
public class Outsider implements Serializable{
private static final long serialVersionUID = 4449726365885112352L;
private String firstName;
private String lastName;
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
Code to save data:-
This code should work as long as you have dynamoDBClient object. I have used Spring to inject the object to my service class. There are multiple ways.
public Boolean createOrderWithOutsider(String productName, Outsider outsider) {
DynamoDBMapper dynamoDBMapper = new DynamoDBMapper(dynamoDBClient);
Order order = new Order();
order.setProductName(productName);
order.setOutsider(outsider);
dynamoDBMapper.save(order);
System.out.println("Order id : " + order.getOrderId());
return true;
}
Test code:-
#Test
public void createOrderWithOutsider() {
Outsider outsider = new Outsider();
outsider.setFirstName("John");
outsider.setLastName("Micheal");
Assert.isTrue(tableOperations.createOrderWithOutsider("Pepsi", outsider));
}
Connection sample:-
<bean id="amazonDynamoDB" class="com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient">
<constructor-arg ref="amazonAWSCredentials" />
<property name="endpoint" value="${amazon.dynamodb.endpoint}" />
</bean>
Autowired in service class:-
#Autowired
private AmazonDynamoDBClient dynamoDBClient;
Sample data saved in table:-

Parse json into multiple java pojos

I am writing a Restful webservice which would receive data in the below format.
{
"myOrder": {
"submitDate": "2015-04-16T02:52:01.406-04:00",
"supplier": "Amazon",
"orderName": "Wifi Router",
"submittedBy": "Gaurav Varma",
"price": {
"value": "2000",
"currency": "USD"
},
"address": {
"name": "My home",
"address": "Unknow island",
"city": "Mainland China",
"state": "Xinjiang",
"contact": {
"firstName": "Gaurav",
"lastName": "Varma",
"phone": "000-000-0000",
"email": "test#gv.com"
}
}
}
}
To read that data I am considering Jackson or GSON frameworks. The easiest way would be to use a Java POJO which has exactly the same structure as the json request. But for me the structure of Java POJOs is different. I have four different pojo as mentioned below :
Submitter.java
- SubmittedBy
- SubmitDate
Order.java
- Supplier
- OrderName
Price.java
- Value
- Currency
Address.java
- Name
- Address
- City
- State
Contact.java
- FirstName
- LastName
- Phone
- Email
Question : Is it a way to parse the json once into five different POJOs. May be some annotation based approach where we can map json attribute to respective pojo attribute? Any framework available for it?
Thanks in advance !
I'm currently using Jackson on my project. You have the option of annotating your POJO fields with #JsonProperty or #JsonUnwrapped. You would use #JsonUnwrapped on Order, for example, and then Order would have two fields (supplier and orderName) that use #JsonProperty.
See here for more details.
You could use eclipse link moxy for this. It uses JAXB style annotations for field to JSON/XML mapping.
Moxy is part of eclipse link.
Wikipedia:
EclipseLink is the open source Eclipse Persistence Services Project
from the Eclipse Foundation. The software provides an extensible
framework that allows Java developers to interact with various data
services, including databases, web services, Object XML mapping (OXM),
and Enterprise Information Systems (EIS).
So in your code you would use it like;
Model A:
#XmlElement(name="completed_in")
public float getCompletedIn() {
return completedIn;
}
Model B:
#XmlElement(name="created_at")
#XmlJavaTypeAdapter(DateAdapter.class)
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
#XmlElement(name="from_user")
public String getFromUser() {
return fromUser;
}
Json:
{
"completed_in":0.153,
{
"created_at":"Fri, 12 Aug 2011 01:14:57 +0000",
"from_user":"stackfeed",
you can use the composition design pattern and have an instance of each object in a wrapper class. Or you can try to parse the json into a map and write code to instantiate and set the variables as needed.
You could use Jackson; I think you need a POJO to wrapp the Order and Address like
class FullOrder {
Order order;
Address address;
public Order getOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
With this you can easily use Jackson
String json; // your json here
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.readValue(json, FullOrder.class);
And that will parse the json into your pojo. Hope it helps you
The full structure
class Submitter {
private Date submittedBy;
private Date submitDate;
public Date getSubmittedBy() {
return SubmittedBy;
}
public void setSubmittedBy(Date submittedBy) {
SubmittedBy = submittedBy;
}
public Date getSubmitDate() {
return SubmitDate;
}
public void setSubmitDate(Date submitDate) {
SubmitDate = submitDate;
}
}
class Order {
private String supplier;
private String orderName;
private Price price;
private Submitter submitter;
public Price getPrice() {
return price;
}
public void setPrice(Price price) {
this.price = price;
}
public Submitter getSubmitter() {
return submitter;
}
public void setSubmitter(Submitter submitter) {
this.submitter = submitter;
}
public String getSupplier() {
return Supplier;
}
public void setSupplier(String supplier) {
Supplier = supplier;
}
public String getOrderName() {
return OrderName;
}
public void setOrderName(String orderName) {
OrderName = orderName;
}
}
class Price {
private int value;
private int currency;
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public int getCurrency() {
return currency;
}
public void setCurrency(int currency) {
this.currency = currency;
}
}
class Address {
private String name;
private String address;
private String city;
private String state;
private Contact contact;
public Contact getContact() {
return contact;
}
public void setContact(Contact contact) {
this.contact = contact;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}
class Contact {
String firstName;
String lastName;
long phone;
String email;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public long getPhone() {
return phone;
}
public void setPhone(long phone) {
this.phone = phone;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
class FullOrder {
Order myOrder;
Address address;
public Order getMyOrder() {
return order;
}
public void setMyOrder(Order order) {
this.order = order;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
This is structure of your json, you only need to copy it and use the Object mapper to parse the json to the pojo (FullOrder) that contains the other pojos and properties
String json; // your json here
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.readValue(json, FullOrder.class);
I figured out the solution approach. Posting for other users. The complete implementation is on my blog - http://javareferencegv.blogspot.com/2015/04/parse-json-into-multiple-java-pojos.html
So basically 3 points regarding solution approach:
We use Jackson annotation - #JsonIgnoreProperties. This would make
sure only those fields in Pojo are mapped to JSON attributes. So we
read the json twice, once mapping to Order.java and then to
Submitter.java. Both gets the correspondingly mapped fields.
We use Jackson annotation - #JsonProperty. This lets us map the exact JSON attribute to a field in POJO. The annotation makes sure different named attributes in JSON and POJO are mapped.
Jackson doesn't provide any annotation to perform #JsonWrapped (The vice-versa #JsonUnwrapped is available for serialization). Hence, we map Price as an attribute in Order.java.
The main class looks like this :
import com.fasterxml.jackson.databind.ObjectMapper;
public class JacksonDeserializer {
public static void main(String[] args) {
try {
// ObjectMapper provides functionality for data binding between
ObjectMapper mapper = new ObjectMapper();
String jsonString = "{\"submitDate\":\"2015-04-16\",\"submittedBy\":\"Gaurav Varma\",\"supplier\":\"Amazon\",\"orderName\":\"This is my order\","
+ "\"price\": {\"value\": \"2000\",\"currency\": \"USD\"}"
+ "}";
System.out.println("JSON String: " + jsonString);
// Deserialize JSON to java format and write to specific POJOs
Submitter submitterObj = mapper.readValue(jsonString, Submitter.class);
Order orderObj = mapper.readValue(jsonString, Order.class);
Price priceObj = orderObj.getPrice();
System.out.println("submitterObj: " + submitterObj);
System.out.println("orderObj: " + orderObj);
System.out.println("priceObj: " + priceObj);
} catch (Exception e) {
e.printStackTrace();
}
}
}

How can I deserialize part of a JSON string into an object, using FlexJSON?

I'm trying to serialze part of a JSON string into an object. The JSON string looks as follows:
{"error":null,"excludeFields":null,"message":null,"success":{"user":{"name":null,"organizationId":100,"username":"nl4321"}}}
I only need the user-part of the JSON string, which corresponds to an object of the following class:
public class UserForm implements Serializable {
private static final long serialVersionUID = -5033294929007794646L;
private String username;
private String name;
#Getter #Setter
private int organizationId;
public UserForm() {
}
public UserForm(User user) {
if (user != null) {
this.username = user.getUsername();
this.name = user.getName();
this.organizationId = user.getDefaultOrganisationId();
}
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
The way I'm currently trying to deserialize it, is as follows:
private UserForm deserializeToJsonResponse(String bodyContent) throws IOException {
JSONDeserializer<UserForm> jsonDeserializer = new JSONDeserializer<UserForm>();
return jsonDeserializer.use("values.success.user", UserForm.class).deserialize(bodyContent, UserForm.class);
}
The use method is provided with both a path and a class, but no matter what path I try, the contents of the UserForm are null after deserialization. I've tried: "user", "success.user", "values.success.user".
Does anyone have an idea what I'm doing wrong and how I can fix it?
I realize there are other solutions out there, like Jackson, but this is part of a large codebase that already uses FlexJSON a lot. Background: I'm trying to write unit tests for an API that's part of the project.

Categories

Resources