Parse json using ObjectMapper where json key contains json as a value - java

I have a class with such structure:
class SomeClass {
private String stringValue;
private Collection<String> collectionValue = new ArrayList<>();
private String jsonStringValue;
private boolean booleanValue;
}
And then I use
objectMapper.readValue(jsonString, SomeClass.class);
to parse this object from JSON.
The main problem is that jsonStringValue is a json inside of json:
{"stringValue" : "someString",
"collectionValue" : ["123456", "234567", "hello"],
"jsonStringValue" : "{
"someKey" : 1,
"anotherKey" : {
"againKey" : "value"
}
},
"booleanValue" : true
}
And trying to parse jsonStringValue it throws
com.fasterxml.jackson.core.JsonParseException: Unexpected character ('a' (code 97)): was expecting comma to separate Object entries
Exactly "a" character from my example (json modified on security purposes)
I believe there should be some escaping rule for parsing json as a String.
How do I parse json value as a string?

First, your JSON string is not valid because there is a redundant double quote before the left bracket in jsonStringValue. The valid one looks like this:
{
"stringValue" : "someString",
"collectionValue" : ["123456", "234567", "hello"],
"jsonStringValue" : {
"someKey" : 1,
"anotherKey" : {
"againKey" : "value"
}
},
"booleanValue" : true
}
Second, jsonStringValue is not a simple String object, it is a nested JSON objects. Therefore, you are supposed to create corresponding classes for it as follows:
Class SomeClass {
private String stringValue;
private List<String> collectionValue = new ArrayList<>();
private JsonStringValue jsonStringValue;
private boolean booleanValue;
//general getters and setters
}
Class JsonStringValue {
private int someKey;
private AnotherKey anotherKey;
//general getters and setters
}
Class AnotherKey {
private String againKey;
//general getters and setters
}
At last, the given JSON string can be transformed into SomeClass POJO with ObjectMapper.
ObjectMapper mapper = new ObjectMapper();
SomeClass someClass = mapper.readValue(jsonStr, SomeClass.class);
System.out.println(someClass.getjsonStringValue().getAnotherKey().getAgainKey());
Console output:
value
UPDATED
If you still want to transform the jsonStringValue object into String, an alternative way is shown as follows:
Create 2 classes - SomeClassOriginal and SomeClass, the only difference between them is the data type of jsonStringValue. The former one is JsonNode and later one is String.
Class SomeClassOriginal {
private String stringValue;
private List<String> collectionValue = new ArrayList<>();
private JsonNode jsonStringValue;
private boolean booleanValue;
//general getters and setters
}
Class SomeClass {
private String stringValue;
private List<String> collectionValue = new ArrayList<>();
private String jsonStringValue;
private boolean booleanValue;
public SomeClass(SomeClassOriginal someClassOriginal) {
super();
this.stringValue = someClassOriginal.stringValue;
this.collectionValue = someClassOriginal.collectionValue ;
this.jsonStringValue= someClassOriginal.jsonStringValue.toString();
this.booleanValue= someClassOriginal.booleanValue;
}
//general getters and setters
}
Then you can get the jsonStringValue as String like this:
ObjectMapper mapper = new ObjectMapper();
SomeClassOriginal someClassOriginal = mapper.readValue(jsonStr, SomeClassOriginal.class);
SomeClass someClass = new SomeClass(SomeClassOriginal);
System.out.println(someClass.getjsonStringValue());
Console output:
{"someKey":1,"anotherKey":{"againKey":"value"}}

Related

Nested Json Object Returns Null When Converting to POJO

I have the below JSON that I'm trying to read into a JSON node and then map to a Java POJO object.
{
"operation": "myoperation",
"input": {
"environment": "myEnv",
"stage": "beta"
}
}
Below is the POJO class
#JsonIgnoreProperties(ignoreUnknown = true)
public class Input {
private String environment;
private String stage;
public String getEnvironment() {
return apolloEnvironment;
}
public String getStage() {
return stage;
}
}
When I read the above tree using the mapping below, I get null for the environment and stage properties.
Input myInput = OBJECT_MAPPER.treeToValue(inputJson, Input.class);
System.out.println(myInput.getEnvironment()); //returns null
System.out.println(myInput.getStage()); //returns null
I would like to read the environment and stage property values, how can I do this with the above logic?
The Input object in your JSON is wrapped by another object. So instead of trying to parse the whole Tree as Input, you need to obtain the Value mapped to the property input and then parse it.
Note that setters should be in place in your Input class.
String jsonString = """
{
"operation": "myoperation",
"input": {
"environment": "myEnv",
"stage": "beta"
}
}
""";
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree(jsonString).get("input");
Input myInput = mapper.treeToValue(node, Input.class);
System.out.println(myInput.getEnvironment());
System.out.println(myInput.getStage());
Output:
myEnv
beta
Another option would be to declare another class, wrapping Input, let's say InputWrapper. The would allow to deserialize the JSON you directly into the InputWrapper instance and then access nested Input via getter method.
Here's how such wrapper class might look like (Lombok's annotations used for brevity):
#Setter
#Getter
public static class InputWrapper {
private String operation;
private Input input;
}
#Setter
#Getter
public static class Input {
private String environment;
private String stage;
}
Parsing logic:
String jsonString = """
{
"operation": "myoperation",
"input": {
"environment": "myEnv",
"stage": "beta"
}
}
""";
ObjectMapper mapper = new ObjectMapper();
InputWrapper inputWrapper = mapper.readValue(jsonString, InputWrapper.class);
Input myInput = inputWrapper.getInput();
System.out.println(myInput.getEnvironment());
System.out.println(myInput.getStage());
Output:
myEnv
beta

Is it possible to deserialize values missing from JSON to some default values?

As an example class:
#Getter #Setter
public static class SomeClass {
private String notNull;
private String nullSetEmpty;
private String notExists;
}
Deserialization of null values to empty is possible by overriding configuration, like:
String json = " {\"notNull\": \"a value\", \"nullSetEmpty\": null}";
ObjectMapper om = new ObjectMapper();
om.configOverride(String.class)
.setSetterInfo(JsonSetter.Value.forValueNulls(Nulls.AS_EMPTY));
SomeClass sc = om.readValue(json, SomeClass.class);
System.out.print(om.writerWithDefaultPrettyPrinter().writeValueAsString(sc));
This produces:
{
"notNull" : "a value",
"nullSetEmpty" : "",
"notExists" : null
}
But how about this notExists. It is possible to add default value to each class having the problem but is there any generic way to do that like configOverride does so that Jackson handles that?
you can just define default value in your data class
#Getter
#Setter
public static class SomeClass {
private String notNull;
private String nullSetEmpty;
private String notExists = "default-value";
}

json serialization with a variable field as a string

I have a pojo that I am unmarshalling a REST response to. One of the fields ("variable value") is just a Json variable element (can be any form).
Is there a way to tell it to treat the field as a plain string for all cases instead of trying to deserialize to an object?
Here's a json obiect ("variable value" can be any form):
{"id":1, "variable value": {"name":"one", "age": 22, "data":{"key":"value"}}}
I would like to save this json as a class object using gson
public class SomeCommand {
private Long id;
private String data;
}
It sounds that you would like to parse the given JSON string to transform variable value into String object. You can achieve this by creating 2 classes - SomeCommandOriginal and SomeCommand as follows:
First, convert the JSON string to SomeCommandOriginal to map the value of variable value to JsonNode.
class SomeCommandOriginal {
private Long id;
#JsonProperty("variable value")
private JsonNode variableValue;
//general getters and setters
}
class SomeCommand {
private Long id;
private String data;
public SomeCommand(SomeCommandOriginal someCommandOriginal) {
super();
this.id = someCommandOriginal.id;
this.data = someCommandOriginal.variableValue.toString();
}
//general getters and setters
}
Second, initialize an instance of SomeCommand and pass someCommandOriginal as the argument of customized constructor:
ObjectMapper mapper = new ObjectMapper();
SomeCommandOriginal someCommandOriginal = mapper.readValue(jsonStr, SomeCommandOriginal.class);
SomeCommand someCommand = new SomeCommand(someCommandOriginal);
System.out.println(someCommand.getData());
Console output:
{"name":"one", "age": 22, "data":{"key":"value"}}
UPDATED
If you are using Gson, just modify the datatype of variableValue to be JsonObject and switch to #SerializedName annotation as follows:
class SomeCommandOriginal {
private Long id;
#SerializedName("variable value")
private JsonObject variableValue;
//general getters and setters
}
And then you can get the same result as well:
Gson gson = new Gson();
SomeCommandOriginal someCommandOriginal = gson.fromJson(jsonStr, SomeCommandOriginal.class);
SomeCommand someCommand = new SomeCommand(someCommandOriginal);
System.out.println(someCommand.getData());

Can't serialize with Jackson Json class fields if it extends ArrayList

I'm using Jackson Json. I can't serialize class fields if class extends ArrayList.
Class:
public class DataElement {
private Date date;
private int val1;
private int val2;
// constructor, getters, setters
}
public class DataArray extends ArrayList<DataElement> {
private String info;
private int num;
// constructor, getters, setters
}
Serialization:
ObjectMapper jsonMapper = new ObjectMapper();
jsonMapper.writeValue(new File("path"), dataArray);
Result file contains DataElements only:
[ {
"date" : 1446405540000,
"val1" : 10296,
"val2" : 30365
}, {
"date" : 1446405600000,
"val1" : 40164,
"val2" : 20222
} ]
'num' and 'info' are not saved into file.
How to save full class including its fields?
Jackson will serialize your POJOs according to the JsonFormat.Shape. For an ArrayList object that is ARRAY. You can change the shape to OBJECT with an annotation.
#JsonFormat(shape = JsonFormat.Shape.OBJECT)
public class DataArray extends ArrayList<DataElement> {
Make sure DataArray has a getter that returns an ArrayList for e.g.
public ArrayList<DataElement> getContents() {
return new ArrayList<>(this);
}
When I tried the above code I saw this field at the resulting JSON
"empty":false
You can use #JsonIgnore to prevent that from appearing

How to access JSON subfield using #JsonProperty annotation?

I need transform json in another diferent json , im using #JsonProperty annotation for change name fields JSON result , but i dont know access fields encapsulate in differents json level for example :
{ "prop1" : "value1",
"prop2" : "value2",
"prop3" : {
"prop4" : "value4",
"prop5" : {
"prop6" : "value6"
}
}
}
json result
{
"prop1_new_name":"value1",
"prop4_new_name":"value4",
"prop6_new_name":"value6"
}
This seems like a continuation of your previous question. So, in addition of using #JsonUnwrapped as explained in the answer, you need to add #JsonProperty on the field in the class where it is declared. Modifying the previous answer with #JsonProperty gives you this:
#RunWith(JUnit4.class)
public class Sample {
#Test
public void testName() throws Exception {
SampleClass sample = new SampleClass("value1", "value2", new SubClass("value4", "value5", new SubSubClass("value7")));
new ObjectMapper().writeValue(System.out, sample);
}
#JsonAutoDetect(fieldVisibility=Visibility.ANY)
public static class SampleClass {
private String prop1;
private String prop2;
#JsonUnwrapped
private SubClass prop3;
public SampleClass(String prop1, String prop2, SubClass prop3) {
this.prop1 = prop1;
this.prop2 = prop2;
this.prop3 = prop3;
}
}
#JsonAutoDetect(fieldVisibility=Visibility.ANY)
public static class SubClass {
#JsonProperty("prop4_new_name")
private String prop4;
private String prop5;
#JsonUnwrapped
private SubSubClass prop6;
public SubClass(String prop4, String prop5, SubSubClass prop6) {
this.prop4 = prop4;
this.prop5 = prop5;
this.prop6 = prop6;
}
}
#JsonAutoDetect(fieldVisibility=Visibility.ANY)
public static class SubSubClass{
#JsonProperty("prop7_new_name")
private String prop7;
public SubSubClass(String prop7) {
this.prop7 = prop7;
}
}
}
With this as a result:
{"prop2":"value2","prop5":"value5","prop7_new_name":"value7","prop4_new_name":"value4","prop1_new_name":"value1"}
"prop3" would be a Map in your Java object when deserializing (if you have it properly annotated). Then you can create a custom JsonSerializer to output your expected result.
To create your custom JsonSerializer, you can follow this guide: http://dev.sghill.net/2012/04/how-do-i-write-jackson-json-serializer.html

Categories