Convert complex JSON to one-level POJO using Jackson - java

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
}

Related

In Java, how can I map a string that is either JSON or XML to the same POJO, but with the XML having one different field/attribute name from the JSON?

I am currently using Jackson's XmlMapper and ObjectMapper. I want to map the string to a POJO (I think I'm using that term correctly) that has a private field with the same name as the JSON string's field. The XML string has a different name for the same field/attribute, and I want to use the JSON field name.
I also want to essentially "ignore" that field (while keeping it) and store it as something like a JsonNode, as the value of that field can be some complex, nested value without a known shape.
Example:
public static class OuterClass {
private String firstValue;
private InnerClass innerValue;
// ... getters/setters
}
public static class InnerClass {
private JsonNode data; // complex, nested, so no POJO to map to
private String otherValue;
// ... getters/setters
}
The JSON might look like this:
{
"innerValue": {
"data": {
... complex stuff
},
"otherValue": "more stuff"
},
"firstValue": "thingy"
}
The XML might look like this:
<result>
<innerValue>
<incorrectName>
... complex stuff
</incorrectName>
<otherValue>more stuff</otherValue>
</innerValue>
<firstValue>thingy</firstValue>
</result>
So the goal is to get the XML to work with that class, including both mapping incorrectName to the class' data, as well as storing the complex inner part as something like a JsonNode since I don't have a class to model it.
I have the JSON working with new ObjectMapper().readValue(jsonString, OuterClass.class), and I think the XML should work with new XmlMapper().readValue(xmlString, OuterClass.class), but I don't know where to go with annotations. I've looked at the different annotations available and I don't think I've found the right one. I've also read that I shouldn't convert XML to a JsonNode, as there can be problems with that. I don't need to convert it back to XML after, though, and can treat it as JSON once I receive the JSON/XML string. So, I'd appreciate some help, thanks!
#XmlAccessorType(XmlAccessType.PROPERTY)
public static class InnerClass {
private JsonNode data; // complex, nested, so no POJO to map to
private String otherValue;
// ... getters/setters
#XmlElement(name = "incorrectName")
protected JsonNode getData() {return data;}
}

Deserialize flattened JSON keys to proper objects with GSON

We ingest an external API (we cannot change the JSON we receive) which produces JSON with flattened keys. For example:
{
"composite.key": "value",
"normal": "another value",
"composite.key2": "back here again..."
}
which we would ideally like to deserialize into:
public class SomeObject {
public String normal;
public Composite composite;
}
public class Composite {
public String key;
public String key2;
}
while we know we can write a custom deserializer, I would first like to check if there is support for this in GSON using annotations or by some other means.
You can use GSON's #SerializedName annotation on the Java fields.
Something like this
public class Composite {
#SerializedName("composite.key")
public String key;
#SerializedName("composite.key2")
public String key2;
}
I think the long and short of this is to just use Jackson which provides built-in support by annotating the wrapping object with #JsonUnwrapped(prefix="composite.").

Unable to deserializer and serialize Java List<> with jackson

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)

Unrecognised Property with Jackson #JsonCreator

I'm trying to use Jackson to convert some JSON into an instance of a class that contains some simple Strings and another class, which I'm using #JsonCreator for. It seems that Jackson isn't able to create the instance of the other class.
The problem is that when I run this code as part of a test:
ObjectMapper mapper = new ObjectMapper();
Player player = mapper.readValue(json.toString(), Player.class);
I get the following exception:
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "characterClass" (class xxx.xxx.Player), not marked as ignorable (2 known properties: "name", "character"])
The JSON I'm trying to parse in my simple test looks like this:
{
"name": "joe",
"characterClass": "warrior",
"difficulty": "easy",
"timesDied": 2
}
I have a class 'Player' that looks a bit like this
public class Player {
#JsonProperty("name")
private String playerName;
#JsonProperty // <-- This is probably wrong
private Character character;
// Some getters and setters for those two fields and more
}
And another class 'Character' that looks like this
public class Character{
private PlayerClass playerClass;
private Difficulty difficulty;
private int timesDied;
#JsonCreator
public Character(#JsonProperty("characterClass") String playerClass,
#JsonProperty("difficulty") String diff,
#JsonProperty("timesDied") int died) {
// Validation and conversion to enums
this.playerClass = PlayerClass.WARRIOR;
this.difficulty = Difficulty.EASY;
this.timesDied = died;
}
// Again, lots of getters, setters, and other stuff
}
For small sets of data like this there would be better ways to structure the whole thing, but I think this works for the purposes of an example. The real code I have is more complex but I wanted to make simple example.
I think I've messed up the Jackson annotations, but I'm not sure what I've done wrong.
You need to specify a creator on Player that matches your JSON input. For example:
#JsonCreator
public static Player fromStringValues(#JsonProperty("name") String name,
#JsonProperty("characterClass") String characterClass,
#JsonProperty("difficulty") String difficulty,
#JsonProperty("timesDied") Integer timesDied) {
Player player = new Player();
player.setPlayerName(name);
player.setCharacter(new Character(characterClass, difficulty, timesDied));
return player;
}
A side note, you can structure your enums like this and Jackson will do the conversion from string to enum for you.

Jackson Custom Serializer for class with annotations

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.

Categories