I have a JSON String and I want to deserialize it to a Java object with a interface data member. The Java object looks like this:
public class Person {
private String id;
private String name;
private AddressInterface addr;
}
Both the Person and the AddressInterface are third party classes, so I can't make any changes to them.
When I used the following to deserialize the JSON string,
objectMapper.readValue(json_file, Person.class)
I got the following exception. It's because the object mapper doesn't know how to deserialize the AddressInterface field. Can someone let me know how to deserialize the string into an Person object in this case? Many Thanks.
abstract types either need to be mapped to
concrete types, have custom deserializer,
or be instantiated with additional type information
AddressInterface is an interface and is considered abstract. Both classes Foo and Bar could implement AddressInterface, but it would be unable to tell which one the data should be deserialized as.
Random idea which might work:
Put the interface in a wrapper. I'm just guessing since I don't know the library context, but maybe something like this. Also there's probably a few typos in here, but it shows the general idea.
public class AbstractSerializable<T> implements Deserialize {
private final String className;
private T obj;
public AbstractSerializable(T obj) {
this.obj = obj;
this.className = obj.getClass().getCardinalName();
}
#Override
public AbstractSerializable deserialize(ObjectMapper objectMapper) {
String clazz = input.readNext(String.class);
return objectMapper.readNext(Class.forName(clazz));
}
}
Edit: This would probably break if you tried to put a lambda in it.
Edit 2: #Hadi Note is correct that Gson would make some things easier, however it would run into the same issues. I did find this article which explains how to fix it when using Gson. It uses a similar approach to my answer, but they have a much better explanation.
With GSON library you can get rid of the boilerplate codes!
You can use GSON library in the link below!
https://www.tutorialspoint.com/gson/gson_quick_guide.htm
the problem is deserializing AddressInterface property because its an interface and I think objectMapper is trying to initilaize it's default constructer like bellow
addr = new AddressInterface();
you can create an empty concrete class which inherits the AddressInterface and use it instead of AddressInterface
public class Adress implements AddressInterface {
...
}
public class Person {
private String id;
private String name;
private Adress addr;
}
I want to restructure an application so that it uses REST instead of an EJB3Factory which wasn't needed in the first place and only makes trouble with the new server.
Basically I have a class Request:
public class Request {
public String name;
public String id;
public List<? extends someObject> list;
// default constructor
...
// non-default constructor
public Request(String name, String id, List<T> list) {
this.name = name;
this.id = id;
this.list = list;
}
The Request gets created and using Gson made into a Json object:
Gson gson = new Gson();
String payload = gson.toJson(Request);
This then gets sent to the REST API on the server. There Jackson deserializes it. I do not have access to the Jackson implementation there and cannot change it to Gson.
What I am basically trying to do now is to get Jackson to use the non-default constructor to deserialize the object. I know I can annotate the non-default constructor like this:
#JsonCreator
public Request(#JsonProperty("name") String name, #JsonProperty("id")
String id, #JsonProperty("list") List<T> list) {
this.name = name;
this.id = id;
this.list = list;
}
The thing is though that the field name of list is set at runtime per reflection and the Gson object that is generated might have it as scenarioName1 for one and scenarioName2 for something else.
I have looked at the different solutions provided here on Stack Overflow but none of them could provide me with a solution for my problem. This seemed most helpful but I cannot under any circumstances use a wrapper property nor can I actually map all possibilities.
Anyone got any idea?
EDIT to include examples:
Example 1:
{"name":"someName","id":"first","someScenarioName":[{...}]}
Example 2:
{"name":"someOtherName","id":"second","differentScenarioName":[{...}]}
Since I'm out of town on business that is the best I can do with right now. It's basically the last field having a different name depending on which scenario was chosen beforehand.
Maybe you can try take a look on Mapper Features. Sincerely I didn't try it yet because I'm at work and so on, but I will send now my example and maybe it can help you:
public class Request {
public String name;
public String id;
public List<? extends T> list;
// default constructor
...
// non-default constructor
public Request(String name, String id, List<T> list) {
this.name = name;
this.id = id;
this.list = list;
}
}
Then to deserialize the object:
ObjectMapper mapper = new ObjectMapper();
mapper.configure(MapperFeature.USE_ANNOTATIONS, false);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.readValue(json, Request.class);
My try is because the deserialization by annotation is true by default, but once you don't have a "list" object most of time, it won't find the field there.
Okay, so I figured out what my problem was. There are other lists in the class and those were the trouble. After annotating each of them with #JsonProperty("theirRespectiveName") it worked like a charm... Now I have to annotate about 100 lines of code and solve some more problems.
I need to serialize a pojo into different json structure depending on whom I am sending request. Also I should be able to configure in some config that how field of pojo are mapped to json properties for a given request.
Can this be achived using jackson?
Is there some library or api to do this?
Edit:
For example:
public class Universal {
private int id;
private Date date;
private String name;
private Inner inner;
private Map<String,Object> others;
private List<Inner> inners;
}
public class Inner {
private String value;
}
now above are two object i need to create dynamic json, one example for some of transformation is below
{
"id":"",//value will be id of Universal
"detials":{
"name":"",//value will be name of Universal
},
"data":[], // array of value(field of Inner) from inners
"ext":{
"prop1":""// value of this field will be some (key1) value from others
}
}
You can use Google Gson and rely on its type adaptors.
http://www.javacreed.com/gson-typeadapter-example/ is a good article from web
This question already has answers here:
Jackson Object Mapper in spring MVC not working
(2 answers)
Closed 9 years ago.
I am getting an instance of Serializable out of some internal APIs. The Serializable instance is actually Long or String, etc. Is there a way to make a DTO that can handle this situation? Using private Serializable value; the JSON ends up with value: {}.
UPDATE
Here is a reduced example of the code in question:
#Controller
public class SomeController
{
//...
public MyDto getInfo(Long id)
{
MyDto result = new MyDto();
Serializable obj = svc.getInfo(id);
// obj is either Long, or String, or one of few more fundamental Java types
result.setValue(obj);
return result;
}
}
public class MyDto
{
private Serializable value;
public void setValue(Serializable info)
{
this.value = value;
}
public Serializable getValue()
{
return value;
}
}
UPDATE 2
I have found the answer to my problem here: https://stackoverflow.com/a/20494813/341065
Note that Jackson does not use java.io.Serializable for anything: there is no real value for adding that. It gets ignored.
Given this, Jackson will see values as equivalent of whatever actual type is (for serialization, i.e. writing JSON); or, when reading, as equivalent of java.lang.Object.
If you know the actual type, you could annotate property with #JsonDeserialize(as=ActualType.class) to give a hint. But if actual values are Strings and Longs, this really should not be needed.
I would like to generate a JSON String from my object:
Gson gson = new Gson();
String json = gson.toJson(item);
Everytime I try to do this, I get this error:
14:46:40,236 ERROR [[BomItemToJSON]] Servlet.service() for servlet BomItemToJSON threw exception
java.lang.StackOverflowError
at com.google.gson.stream.JsonWriter.string(JsonWriter.java:473)
at com.google.gson.stream.JsonWriter.writeDeferredName(JsonWriter.java:347)
at com.google.gson.stream.JsonWriter.value(JsonWriter.java:440)
at com.google.gson.internal.bind.TypeAdapters$7.write(TypeAdapters.java:235)
at com.google.gson.internal.bind.TypeAdapters$7.write(TypeAdapters.java:220)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:89)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:200)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:96)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:60)
at com.google.gson.Gson$FutureTypeAdapter.write(Gson.java:843)
These are the attributes of my BomItem class:
private int itemId;
private Collection<BomModule> modules;
private boolean deprecated;
private String partNumber;
private String description; //LOB
private int quantity;
private String unitPriceDollar;
private String unitPriceEuro;
private String discount;
private String totalDollar;
private String totalEuro;
private String itemClass;
private String itemType;
private String vendor;
private Calendar listPriceDate;
private String unitWeight;
private String unitAveragePower;
private String unitMaxHeatDissipation;
private String unitRackSpace;
Attributes of my referenced BomModule class:
private int moduleId;
private String moduleName;
private boolean isRootModule;
private Collection<BomModule> parentModules;
private Collection<BomModule> subModules;
private Collection<BomItem> items;
private int quantity;
Any idea what causes this error? How can I fix it?
That problem is that you have a circular reference.
In the BomModule class you are referencing to:
private Collection<BomModule> parentModules;
private Collection<BomModule> subModules;
That self reference to BomModule, obviously, not liked by GSON at all.
A workaround is just set the modules to null to avoid the recursive looping. This way I can avoid the StackOverFlow-Exception.
item.setModules(null);
Or mark the fields you don't want to show up in the serialized json by using the transient keyword, eg:
private transient Collection<BomModule> parentModules;
private transient Collection<BomModule> subModules;
I had this problem when I had a Log4J logger as a class property, such as:
private Logger logger = Logger.getLogger(Foo.class);
This can be solved by either making the logger static or simply by moving it into the actual function(s).
If you're using Realm and you get this error, and the object giving the trouble extends RealmObject, don't forget to do realm.copyFromRealm(myObject) to create a copy without all the Realm bindings before passing through to GSON for serialization.
I'd missed doing this for just one amongst a bunch of objects being copied... took me ages to realise as the stack trace doesn't name the object class/type. Thing is, the issue is caused by a circular reference, but it's a circular reference somewhere in the RealmObject base class, not your own subclass, which makes it harder to spot!
As SLaks said StackOverflowError happen if you have circular reference in your object.
To fix it you could use TypeAdapter for your object.
For example, if you need only generate String from your object you could use adapter like this:
class MyTypeAdapter<T> extends TypeAdapter<T> {
public T read(JsonReader reader) throws IOException {
return null;
}
public void write(JsonWriter writer, T obj) throws IOException {
if (obj == null) {
writer.nullValue();
return;
}
writer.value(obj.toString());
}
}
and register it like this:
Gson gson = new GsonBuilder()
.registerTypeAdapter(BomItem.class, new MyTypeAdapter<BomItem>())
.create();
or like this, if you have interface and want to use adapter for all its subclasses:
Gson gson = new GsonBuilder()
.registerTypeHierarchyAdapter(BomItemInterface.class, new MyTypeAdapter<BomItemInterface>())
.create();
My answer is a little bit late, but I think this question doesn't have a good solution yet. I found it originally here.
With Gson you can mark the fields you do want to be included in json with #Expose like this:
#Expose
String myString; // will be serialized as myString
and create the gson object with:
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
Circular references you just do not expose. That did the trick for me!
This error is common when you have a logger in your super class. As #Zar suggested before, you can use static for your logger field, but this also works:
protected final transient Logger logger = Logger.getLogger(this.getClass());
P.S. probably it will work and with #Expose annotation check more about this here: https://stackoverflow.com/a/7811253/1766166
I have the same problem. In my case the reason was that constructor of my serialized class take context variable, like this:
public MetaInfo(Context context)
When I delete this argument, error has gone.
public MetaInfo()
Edit: Sorry for my bad, this is my first answer. Thanks for your advises.
I create my own Json Converter
The main solution I used is to create a parents object set for each object reference. If a sub-reference points to existed parent object, it will discard.
Then I combine with an extra solution, limiting the reference time to avoid infinitive loop in bi-directional relationship between entities.
My description is not too good, hope it helps you guys.
This is my first contribution to Java community (solution to your problem). You can check it out ;)
There is a README.md file
https://github.com/trannamtrung1st/TSON
For Android users, you cannot serialize a Bundle due to a self-reference to Bundle causing a StackOverflowError.
To serialize a bundle, register a BundleTypeAdapterFactory.
In Android, gson stack overflow turned out to be the declaration of a Handler. Moved it to a class that isn't being deserialized.
Based on Zar's recommendation, I made the the handler static when this happened in another section of code. Making the handler static worked as well.
BomItem refers to BOMModule (Collection<BomModule> modules), and BOMModule refers to BOMItem (Collection<BomItem> items). Gson library doesn't like circular references. Remove this circular dependency from your class. I too had faced same issue in the past with gson lib.
I had this problem occur for me when I put:
Logger logger = Logger.getLogger( this.getClass().getName() );
in my object...which made perfect sense after an hour or so of debugging!
Avoid unnecessary workarounds, like setting values to null or making fields transient. The right way to do this, is to annotate one of the fields with #Expose and then tell Gson to serialize only the fields with the annotation:
private Collection<BomModule> parentModules;
#Expose
private Collection<BomModule> subModules;
...
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
I had a similar issue where the class had an InputStream variable which I didn't really have to persist. Hence changing it to Transient solved the issue.
After some time fighting with this issue, I believe i have a solution.
Problem is in unresolved bidirectional connections, and how to represent connections when they are being serialized.
The way to fix that behavior is to "tell" gson how to serialize objects. For that purpose we use Adapters.
By using Adapters we can tell gson how to serialize every property from your Entity class as well as which properties to serialize.
Let Foo and Bar be two entities where Foo has OneToMany relation to Bar and Bar has ManyToOne relation to Foo. We define Bar adapter so when gson serializes Bar, by defining how to serialize Foo from perspective of Bar cyclic referencing will not be possible.
public class BarAdapter implements JsonSerializer<Bar> {
#Override
public JsonElement serialize(Bar bar, Type typeOfSrc, JsonSerializationContext context) {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("id", bar.getId());
jsonObject.addProperty("name", bar.getName());
jsonObject.addProperty("foo_id", bar.getFoo().getId());
return jsonObject;
}
}
Here foo_id is used to represent Foo entity which would be serialized and which would cause our cyclic referencing problem. Now when we use adapter Foo will not be serialized again from Bar only its id will be taken and put in JSON.
Now we have Bar adapter and we can use it to serialize Foo. Here is idea:
public String getSomething() {
//getRelevantFoos() is some method that fetches foos from database, and puts them in list
List<Foo> fooList = getRelevantFoos();
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Bar.class, new BarAdapter());
Gson gson = gsonBuilder.create();
String jsonResponse = gson.toJson(fooList);
return jsonResponse;
}
One more thing to clarify, foo_id is not mandatory and it can be skipped. Purpose of adapter in this example is to serialize Bar and by putting foo_id we showed that Bar can trigger ManyToOne without causing Foo to trigger OneToMany again...
Answer is based on personal experience, therefore feel free to comment, to prove me wrong, to fix mistakes, or to expand answer. Anyhow I hope someone will find this answer useful.