In my project, I'm facing to a problem of JSON serialization. I have made a quick simple example to illustrate it:
I have created an object Session which contains a hashMap.
public class session {
private Map<String, Object> mapKeyValue = new HashMap<>();
public Map<String, Object> getMapKeyValue() {
return mapKeyValue;
}
public void setMapKeyValue(Map<String, Object> mapKeyValue) {
this.mapKeyValue = mapKeyValue;
}
}
This map can contains various object. I only know the type of these objects at runtime. Example :
public class Dog {
private final String race;
private final String name;
public Dog(String race, String name) {
this.name = name;
this.race = race;
}
public String getName() {
return name;
}
public String getRace() {
return race;
}
}
or
public class Insect {
private final int nbPates;
private final String name;
public Insect(int nbPates, String name) {
this.nbPates = nbPates;
this.name = name;
}
public int getNbPates() {
return nbPates;
}
public String getName() {
return name;
}
}
I want to save my object session to a database to be able to restore it later.
My first idea was to serialize my session object in json and then to save to to database.
public static void main(String[] args) {
final session session = new session();
session.add("Dog", new Dog("labrador", "milou"));
session.add("Insect", new Insect(4, "cafar"));
String output = serializeJSON(session);
final session session2 = (session) deserializeJSON(output, session.class);
Dog dog = (Dog)session2.get("Dog");
}
It works well. When i deserialize it, there is no error, but as a result, the map contained in the deserialized Session doesn't contain the original type of object but contains hashMap$Node objects.
I guess the deserialization can not guess the types of objects from the string.
Is there a workAround or any other strategy to save my session to database and to be able to restore it as it was previously ?
Related
I am trying to serialise an object by converting it first to another object. At the same time I do not want to serialise twice the same object so I am using #JsonIdentityReference to serialise it only the first time. However since the converter will always create a new output object for the same input object the entire object is serialised every time. Is there a way to avoid serialising twice the same object while at the same time using a converter?
The Domain class
#JsonSerialize(converter = BooleanWithNameConverter.class)
#JsonDeserialize(converter = BooleanWithNameDtoConverter.class)
public class BooleanWithName {
private final boolean value;
private final String name;
BooleanWithName(String name, boolean value) {
this.name = name;
this.value = value;
}
public String getName() {
return name;
}
public boolean getValue() {
return value;
}
}
The DTO
#JsonIdentityInfo(generator = ObjectIdGenerators.UUIDGenerator.class)
public class BooleanWithNameDto {
private final boolean value;
private final String name;
#JsonCreator
BooleanWithNameDto(#JsonProperty("name") String name, #JsonProperty("value") boolean value) {
this.name = name;
this.value = value;
}
public String getName() {
return name;
}
public boolean getValue() {
return value;
}
}
The converter to the DTO
public class BooleanWithNameConverter extends StdConverter<BooleanWithName, BooleanWithNameDto> {
#Override
public BooleanWithNameDto convert(BooleanWithName obj) {
return new BooleanWithNameDto(obj.getName(), obj.getValue());
}
}
The converter back to the domain object
This is not strictly needed since the problem can be seen just by examining the json string but helpful in order to add a small unit test demonstrating the problem
public class BooleanWithNameDtoConverter extends StdConverter<BooleanWithNameDto, BooleanWithName> {
#Override
public BooleanWithName convert(BooleanWithNameDto obj) {
return new BooleanWithName(obj.getName(), obj.getValue());
}
}
A small unit test that fails
public class UnitTest {
#Test
public void testConverter() throws JsonProcessingException {
BooleanWithName booleanWithName = new BooleanWithName("dummy", true);
ObjectMapper objectMapper = new ObjectMapper();
BooleanWithName[] value = { booleanWithName, booleanWithName };
BooleanWithName[] readValue = objectMapper.readValue(objectMapper.writeValueAsString(value), BooleanWithName[].class);
Assertions.assertEquals(readValue[0], readValue[1]);
}
}
Thanks a lot!
I want to convert below JSON structure to java object, Annotation bases.
What will be the pojo java class structure?
{
"Data1":{
"Name":"abc",
"Number":2
}
}
Data1 can by any string-like if it coming as data1 first time, next time it can be like "xyz".
How can we convert it using fasterxml json annotations?
class Node {
public String name;
public int number
}
class ConvertedPojo {
public Map<String, Node> attributes;
}
Since Data1 can be any string you need a map which will store all different string as key and value as json object
Class structure will be :
public class Data1{
#JsonProperty("Name")
private String name;
#JsonProperty("Number")
private int number;
public String getName() {
return name;
}
public void setName(String name) {
name = name;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
number = number;
}
}
public class Data {
#JsonProperty("Data1")
Object data1;
public Object getData1() {
return data1;
}
public void setData1(Object data1) {
this.data1 = data1;
}
}
Take care of the variable naming convention.
Code to test:
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
Data data1 = mapper.readValue("{\"Data1\":{\"Name\":\"abc\",\"Number\":2}}", Data.class);
System.out.println(mapper.writeValueAsString(data1));//{"Data1":{"Name":"abc","Number":2}}
Data data2 = mapper.readValue("{\"Data1\":\"data value\"}", Data.class);
System.out.println(mapper.writeValueAsString(data2));//{"Data1":"data value"}
}
I am learning about immutable Objects. I am trying this code
public final class ImmutableObject {
private final String name;
private final NormalObject obj = new NormalObject();
public String getName() {
return name;
}
public ImmutableObject(String name) {
this.name = name;
obj.setName(name);
}
public NormalObject getObj() {
NormalObject tempObj = obj;
return tempObj;
}
}
public class NormalObject {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
I want to restrict the calling class from changing the value of name variable of NormalObject
But the following code changes the value
ImmutableObject obj = new ImmutableObject("Siddle");
System.out.println(obj.getObj().getName()); //prints Siddle
obj.getObj().setName("Kelly");
System.out.println(obj.getObj().getName()); //prints Kelly
How to restrict it?
For an object to be immutable, all of its properties must be immutable. Its state must not be changeable.
To do that, you have to put an immutable facade on NormalObject, you can't directly return a NormalObject. The method that returns it will also need a different return type, you can't return NormalObject but actually return something that doesn't behave like a NormalObject.
E.g.:
public final class ImmutableObject {
private final String name;
private final NormalObject obj = new NormalObject();
private final ImmutableNormalObject objFacade = new ImmutableNormalObject(obj);
public String getName() {
return name;
}
public ImmutableObject(String name) {
this.name = name;
obj.setName(name);
}
public ImmutableNormalObject getObj() {
return objFacade;
}
}
public class NormalObject {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class ImmutableNormalObject {
private NormalObject obj;
public ImmutableNormalObject(Normalobject o) {
this.obj = o;
}
public String getName() {
return obj.getName();
}
}
Alternately, if it's acceptable to copy the object and it has a copy constructor (or you can add one), you could do that, but copy-and-return is expensive.
You can do this by returning a copy of your normalObject in getter:
public NormalObject getObj() {
return new NormalObject(obj.getName());
// or you can make a copy constructor:
// return new NormalObject(obj);
}
Or you can make a wrapper for your NormalObject that ignores name setter, but it brakes logic.
Please change Your NormalObject code to
public final class ImmutableObject {
private final String name;
// initialise it to null
private final NormalObject obj = null;
public String getName() {
return name;
}
public ImmutableObject(String name) {
this.name = name;
// use the Constructor for setting name only once during initialization of ImmutableObject via its constructor
obj = new NormalObject(name);
//obj.setName(name);
}
public NormalObject getObj() {
NormalObject tempObj = obj;
return tempObj;
}
}
NormalObject Class
public class NormalObject {
private String name;
public NormalObject(name){
this.name = name;
}
public String getName() {
return name;
}
//Remove any setter on NormalObject
/*public void setName(String name) {
this.name = name;
}*/
}
In an immutable object, if a User tries to change the state of the Object. either you won't allow or return a new Instance of the Immutable class.
So, since Date is a mutable class.
You can create an immutable wrapper around date, and you can expose only those methods, that are subject to be used in your Immutable-Date's perspective, but you return a new instance of your Immutable class, with the changed attribute of your new Date.
I don't think final would be required for Immutable variable, because it is already private and Immutable.
Example :
public class Main{
private ImmutableDate immutableDate;
public Main() {
this.immutableDate = new ImmutableDate(new Date());
}
public Main(Date date){
this.immutableDate = new ImmutableDate(date);
}
public ImmutableDate getDate() {
return immutableDate;
}
public class ImmutableDate{
// private constructor, so this can only be instantiated within the outer class
// therefore, final keyword not required for Date, as no access given to the variable
private Date date;
private ImmutableDate(Date date) {
this.date = date;
}
// Example methods from Date, that are required for our Immutable implementation
public Main setTime(long time){
Date date1 = new Date();
date1.setTime(time);
return new Main(date1);
}
#Override
public String toString() {
return date.toString();
}
}
}
After going through similar questions, I am not still able to solve this problem. I am using Jackson to serialize and deserialize class that does not have matching getters/setters method.
public class Products implements Serializable {
private static final long serialVersionUID = 1L;
#JsonProperty
private final Map<Product, String> prices;
public Products() {
this.prices = new HashMap<>();
}
public void addPrice(final Product product, final String price) {
prices.put(product, price);
}
}
Product Class:
public class Product implements Serializable {
private static final long serialVersionUID = 1L;
#JsonProperty
private final String name;
#JsonProperty
private final String type;
public Product(final String price, final String type) {
this.name = price;
this.type = type;
}
public String getName() {
return name;
}
public String getType() {
return type;
}
public static void main(String[] args) {
Products products = new Products();
products.addPrice(new Product("apple", "fruit"), "16");
JsonDataConverter converter = new JsonDataConverter();
String json = converter.toData(products);
System.out.println(json);
Products deserializedProducts = converter.fromData(json, Products.class);
}
}
JsonDataConverter I am using is from AWS Flow Framework: http://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/simpleworkflow/flow/JsonDataConverter.html
Exception I am getting at deserialization step is: Can not find a (Map) Key deserializer for type [simple type, class mypackage.Product] when mapping key "null".
I am not able to understand this since my prices map does not contain any null values. Strange thing is, It's working fine if Product has only 1 member (just name) field.
Any idea what's is going wrong?
Thanks
Can u try modify the constructor of product and products classes to have JsonCreator annotation and annotate each param with JsonProperty.
Example:
#JsonCreator
public Product(#JsonProperty("price") final String price, #JsonProperty("type") final String type) {
this.name = price; this.type = type;
}
How to assign value to this function in Java incompatible types?
public class CustomerInfo implements Serializable {
private static final long serialVersionUID = 9083257536541L;
protected String id;
protected String searchkey;
protected String taxid;
protected String name;
protected String postal;
/** Creates a new instance of UserInfoBasic */
public CustomerInfo(String id) {
this.id = id;
this.searchkey = null;
this.taxid = null;
this.name = null;
this.postal = null;
}
public String getId() {
return id;
}
public String getTaxid() {
return taxid;
}
public void setTaxid(String taxid) {
this.taxid = taxid;
}
public String getSearchkey() {
return searchkey;
}
public void setSearchkey(String searchkey) {
this.searchkey = searchkey;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPostal() {
return postal;
}
public void setPostal(String postal) {
this.postal = postal;
}
public String printTaxid() {
return StringUtils.encodeXML(taxid);
}
public String printName() {
return StringUtils.encodeXML(name);
}
#Override
public String toString() {
return getName();
}
}
private CustomerInfo selectedCustomer;
public CustomerInfo getSelectedCustomer() {
// construct a CustomerInfo from the data in your String
return selectedCustomer;
}
private void jcmdOKActionPerformed(java.awt.event.ActionEvent evt) {
selectedCustomer = (CustomerInfo) jListCustomers.getSelectedValue();
//test code
String testing = m_jtxtName.getText();
System.out.println("Now the selectedCustomer is dispayed!");
System.out.println(selectedCustomer);
System.out.println(testing);
//test code
dispose();
}
In the above shown code, I need the string testing value to be assigned to selectedCustomer. How can I assign the value? This is the error I get:
selectedCustomer = m_jtxtName.getText();
incompatible types
required: CustomerInfo
found: String
You can't!!!
selectedCustomer is an object of type CustomerInfo.
m_jtxtName.getText() returns a String
You can't assign a String to a CustomerInfo.
Probably you need to do something like:
int id = 1; //Or whatever new id you have.
String name = m_jtxtName.getText();
selectedCustomer = new CustomerInfo(name); //or whatever id you have.
selectedCustomer.setName(name); //or whatever name you have.
EDIT:
Something is missing from your class. Either it needs setter methods (it has only getters now, so you can't set other properties as name etc) or it needs a constructor with four arguments like:
public CustomerInfo(String id, String searchKey, String taxid, String name, String postal) {
this.id = id;
this.searchKey = searchKey;
// etc
In this case, you might have six jtextfields in your screen, so te user can fill all fields and the create the Customerinfo object by passing all parameters to the constructor.
you cannot do it by simply casting a String to a CustomerInfo object, but you could extend your CustomerInfo but you could try something like this:
public class CustomerInfo {
[...]
public static CustomerInfo createCustomerInfo(String data) {
// construct a CustomerInfo from the data in your String
return createdCustomerInfo;
}
}
I don't know what data you have in that String so i can not give you an advice how to implement this. e.g. If it is the ID you could use this to retrieve the CustomerInfo from database or something like that.