I am trying to deserializer and then serialize a java object.
I got an object like this one-
public class Blas{
private Integer blasRootId;
private List<Bla> blaList = new ArrayList<>();
public Blas() {}
/region g & s
getter and setters ..
//endregion
}
And the object -
public class Bla{
private String fileName;
private String description;
private Integer id;
public Bla() {}
//region g & s
getter and setters ..
//endregion
}
I deserialize the object with
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
String jsonString = mapper.writeValueAsString(Blas);
And the created json is like
{
"Blas": {
"blasRootId": 2840,
"blaList": [
"java.util.ArrayList",
[
{
"fileName": "RegularPayload",
"description": "",
"id": 2260
}
]
]
}
}
So when I try to serialize the created json the following error accord -
Can not instantiate value of type [simple type, class Bla] from String value ('java.util.ArrayList'); no single-String constructor/factory method
Who can i make the deserializer to write the list as it is ,without the addition "java.util.ArrayList" list, or how can i read it right?
Update :
It was my mistake, I added in the "mapper.configure" a parameter (That i don't recall which) that caused the serializer to add the "java.util.ArrayList".
My code example should work fine.
As prsmax asked, it depends how the code were you try to deserialize blas looks like, it seems like you are trying to take the string of blas and deserialize like this:
mapper.readValue(blasStr, Bla.class)
If you want only to deserialize a list of Bla you can do this:
JavaType javaType = mapper.getTypeFactory().constructCollectionType(ArrayList.class, ValueType.class);
List<ValueType> list = mapper.readValue(str, javaType);
If you actually need the wrapper object Blas, then you should have a constructor marked with #JsonCreator accepting the a List<Bla> marked with #JsonProperty (There are other ways, but this is a fail safe way and makes the code readable)
Related
I'm using Gson to cast my class to Json, I have a field called payload
which must to be my class itself.
My class is as follow:
public class MyClass {
private Long id;
private String name;
private Object payload;
}
But when I use it as follow:
MyClass myClassObj = MyClass()
myClassObj.setId(1L);
myClassObj.setName("Example");
myClassObj.setPayload(myClassObj);
And I see the result of:
String result = new Gson().toJson(myClassObj);
The result does not contain payload data object.
{"id":1, "name":"Example"}
I need something like:
{"id":1, "name":"Example", "payload": {"id":1, "name":"Example"}}
Thanks in advance.
Any help will be useful.
You will need to change it to be defined as such
public class MyClass {
private Long id;
private String name;
private MyClass payload = null;
}
Gson serialized parameters that are not transient. Object does not have the "id" and "name" parameters you are wanting to serialize. MyClass does. Ensure that payload's default value is null otherwise you may have an infinite loop on your hands when serializing to json.
I found a solution, Object can be anything, so before to set payload of my class I made the follow:
myClassObj.setPayload(new Gson.fromJson(myClassObj.toString(), myClassObj.class);
Note:
In MyClass I have override toString method as follow:
#Override
public String toString() {
return new Gson().toJson(this);
}
If someone has a better solution, feel free to post it.
Let's imagine you have the following JSON:
{
"prop0": "value0",
"level1" : {
"prop1": "value1"
"prop2": "value2"
},
....
}
Can it be turned to simple Java object?
class Pojo {
private String prop0;
private String prop1;
private String prop2;
}
I don't want to create a intermediate class to wrap "level1".
What is come to my mind is to map my class in this way:
class Pojo {
private String prop0;
#JsonProperty("level1.prop1")
private String prop1;
#JsonProperty("level1.prop2")
private String prop2;
}
But unfortunately it doesn't work. The inverse problem - turn complex Java object to plain JSON can be simply solved using #JsonUnwrapped annotation.
Can you please suggest any workable solution for my issue?
You need to either write a custom deserializer, or add a setter that can transform the structure. For latter you could do something like
...
public void setLevel1(Map<String,String> values) { // or JsonNode
prop1 = values.get("prop1");
// and so forth; if names are regular, can use looping
}
I have a complex object and for some of the nested objects I need to serialize them into JSON fields instead of JSON objects.
Eg.
public class Outer {
private String someField;
private AnotherClass anotherField;
}
public class AnotherClass {
#XmlElement(name = "useThisName")
private String someField;
private String anotherField;
}
How can I make a custom serializer that will be for the nested object and obey the annotations so the fields are named properly?
My use case for this is to use the ObjectMapper.convertValue() method to create a Map so that I can loop through it and create NameValuePairs for a rest url.
In the end I am hoping to end up with a
Map<String, String>
That I can loop over and create apache BasicNameValuePairs from.
Below is some code I want to use for the end result if I can get everything to serialize properly.
Map<String, String> parameters
= DefaultJacksonMapper.getDefaultJacksonMapper().convertValue(obj, LinkedHashMap.class);
return parameters
.entrySet()
.stream()
.map(entry -> new BasicNameValuePair(entry.getKey(), entry.getValue()))
.collect(Collectors.toList());
If I convert this to a map now my output is like:
"someField" -> "data"
"anotherField" -> "size = 2"
I am trying to get the Map to have the following output which I feel like I need a custom serializer.
"someField" -> "data"
"useThisName" -> "data"
"anotherField" -> "data"
Ok I figured this out.
I ended up creating a new Module that inherited off of SimpleModule. Then I created a new Abstract class like
public abstract class OuterMixin {
#JsonUnwrapped
private AnotherClass anotherField;
}
I also had to annotate the AnotherClass with JsonProperty Like:
public class AnotherClass {
#XmlElement(name = "useThisName")
#JsonProperty("useThisName")
private String someField;
private String anotherField;
}
The when I got my Object Mapper I just registered my module with it and did the conversion and it all worked out.
As a side note I have another property that I had to write a custom serializer for and the #JsonUnwrapped did not work with that.
I want to deserialize json objects to specific types of objects (using Gson library) based on type field value, eg.:
[
{
"type": "type1",
"id": "131481204101",
"url": "http://something.com",
"name": "BLAH BLAH",
"icon": "SOME_STRING",
"price": "FREE",
"backgroundUrl": "SOME_STRING"
},
{
....
}
]
So type field will have different (but known) values. Based on that value I need to deserialize that json object to appropriate model object, eg.: Type1Model, Type2Model etc.
I know I can easily do that before deserialization by converting it to JSONArray, iterate through it and resolve which type it should be deserialized to. But I think it's ugly approach and I'm looking for better way. Any suggestions?
You may implement a JsonDeserializer and use it while parsing your Json value to a Java instance. I'll try to show it with a code which is going to give you the idea:
1) Define your custom JsonDeserializer class which creates different instance of classes by incoming json value's id property:
class MyTypeModelDeserializer implements JsonDeserializer<MyBaseTypeModel> {
#Override
public MyBaseTypeModel deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context)
throws JsonParseException {
JsonObject jsonObject = json.getAsJsonObject();
JsonElement jsonType = jsonObject.get("type");
String type = jsonType.getAsString();
MyBaseTypeModel typeModel = null;
if("type1".equals(type)) {
typeModel = new Type1Model();
} else if("type2".equals(type)) {
typeModel = new Type2Model();
}
// TODO : set properties of type model
return typeModel;
}
}
2) Define a base class for your different instance of java objects:
class MyBaseTypeModel {
private String type;
// TODO : add other shared fields here
}
3) Define your different instance of java objects' classes which extend your base class:
class Type1Model extends MyBaseTypeModel {
// TODO: add specific fields for this class
}
class Type2Model extends MyBaseTypeModel {
// TODO: add specific fields for this class
}
4) Use these classes while parsing your json value to a bean:
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(MyBaseTypeModel.class, new MyTypeModelDeserializer());
Gson gson = gsonBuilder.create();
MyBaseTypeModel myTypeModel = gson.fromJson(myJsonString, MyBaseTypeModel.class);
I can not test it right now but I hope you get the idea. Also this link would be very helpful.
#stephane-k 's answer works, but it is a bit confusing and could be improved upon (see comments to his answer)
Copy https://github.com/google/gson/blob/master/extras/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java into your project. (It's ok; these classes are designed to be copy/pasted https://github.com/google/gson/issues/845#issuecomment-217231315)
Setup model inheritance:
// abstract is optional
abstract class BaseClass {
}
class Type1Model extends BaseClass {
}
class Type2Model extends BaseClass {
}
Setup GSON or update existing GSON:
RuntimeTypeAdapterFactory<BaseClass> typeAdapterFactory = RuntimeTypeAdapterFactory
.of(BaseClass.class, "type")
.registerSubtype(Type1Model.class, "type1")
.registerSubtype(Type2Model.class, "type2");
Gson gson = new GsonBuilder().registerTypeAdapterFactory(typeAdapterFactory)
.create();
Deserialize your JSON into base class:
String jsonString = ...
BaseClass baseInstance = gson.fromJson(jsonString, BaseClass.class);
baseInstance will be instanceof either Type1Model or Type2Model.
From here you can either code to an interface or check instanceof and cast.
use https://github.com/google/gson/blob/master/extras/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java
then configure it with
public static final class JsonAdapterFactory extends
RuntimeTypeAdapterFactory<MediumSummaryInfo> {
public JsonAdapterFactory() {
super(MyBaseType.class, "type");
registerSubtype(MySubtype1.class, "type1");
registerSubtype(MySubtype2.class, "type2");
}
}
and add the annotation:
#JsonAdapter(MyBaseType.JsonAdapterFactory.class)
to MyBaseType
Much better.
If you have a lot of sub types and you do not want to or cannot maintain a list of them, you can also use an annotation based approach.
Here is the required code and also some usage examples:
https://gist.github.com/LostMekka/d90ade1fe051732d6b4ac60deea4f9c2
(it is Kotlin, but can easily be ported to Java)
For me, this approach is especially appealing, since I write a small library that does not know all possible sub types at compile time.
I have a JSON Structure looking like this:
[
{
"id": 0,
"name": "Foo"
},
{
"id": 1,
"name": "Bar"
}
]
and a corresponding Java Object for Data binding:
public class Thing {
public int id;
public String name;
}
I know how I could deserialize the JSON list into a list of Thing.
Now here comes the tricky part: What I want to do is deserializing the JSON into a class looking like the following snippet by only doing changes to this class:
public class Things {
private List<Thing> things;
public void setThings(List<Thing> things) {
this.things = things;
}
public List<Thing> getThings() {
return this.things;
}
}
This is because the JSON deserialization is build in deep in our application by using an ObjectMapper like this:
private static <T> T parseJson(Object source, Class<T> t) {
TypeReference<T> ref = new TypeReference<T>() {
};
TypeFactory tf = TypeFactory.defaultInstance();
//[...]
obj = mapper.readValue((String) source, tf.constructType(ref));
//[...]
return obj;
}
Are there any annotations with which I can achieve what I want or do I have to make changes to the mapper-code?
Much thanks in advance, McFarlane
The whole point of TypeReference, as described in this link, is to use the generic type argument to retrieve type information.
Internally it does the following
Type superClass = getClass().getGenericSuperclass();
...
_type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
where getActualTypeArguments()[0] will give you the actual type argument. In this case, that will be the type variable T, regardless of what you pass in for the Class<T> t parameter of your method.
The proper usage is
TypeReference<List<Thing>> ref = new TypeReference<List<Thing>>() {};
...
List<Thing> thingsList = ...;
Things things = new Things();
things.setThings(thingsList);
In other words, no, you'll need to change your mapper code to achieve what you want.
As far as I know, you won't be able to map a root JSON array as a property of a class. The alternatives is the TypeReference example above or some other ones found here.