How deserialize json object array as array of json strings? - java

Consider json input:
{
companies: [
{
"id": 1,
"name": "name1"
},
{
"id": 1,
"name": "name1"
}
],
nextPage: 2
}
How deserialize this into class:
public class MyClass {
List<String> companies;
Integer nextPage;
}
Where List<String> companies; consists of strings:
{"id": 1,"name": "name1"}
{"id": 1,"name": "name1"}
#JsonRawValue doesn't work for List<String> companies;
Is there a way to configure Jackson serialization to keep companies array with raw json string with annotations only? (E.g. without writing custom deserializator)

There is no annotation-only solution for your problem. Somehow you have to convert JSON Object to java.lang.String and you need to specify that conversion.
You can:
Write custom deserializer which is probably most obvious solution but forbidden in question.
Register custom com.fasterxml.jackson.databind.deser.DeserializationProblemHandler and handle com.fasterxml.jackson.databind.exc.MismatchedInputException situation in more sophisticated way.
Implement com.fasterxml.jackson.databind.util.Converter interface and convert JsonNode to String. It is semi-annotational way to solve a problem but we do not implement the worst part - deserialisation.
Let's go to point 2. right away.
2. DeserializationProblemHandler
Solution is pretty simple:
ObjectMapper mapper = new ObjectMapper();
mapper.addHandler(new DeserializationProblemHandler() {
#Override
public Object handleUnexpectedToken(DeserializationContext ctxt, JavaType targetType, JsonToken t, JsonParser p, String failureMsg) throws IOException {
if (targetType.getRawClass() == String.class) {
// read as tree and convert to String
return p.readValueAsTree().toString();
}
return super.handleUnexpectedToken(ctxt, targetType, t, p, failureMsg);
}
});
Read a whole piece of JSON as TreeNode and convert it to String using toString method. Helpfully, toString generates valid JSON. Downside, this solution has a global scope for given ObjectMapper instance.
3. Custom Converter
This solution requires to implement com.fasterxml.jackson.databind.util.Converter interface which converts com.fasterxml.jackson.databind.JsonNode to String:
class JsonNode2StringConverter implements Converter<JsonNode, String> {
#Override
public String convert(JsonNode value) {
return value.toString();
}
#Override
public JavaType getInputType(TypeFactory typeFactory) {
return typeFactory.constructType(new TypeReference<JsonNode>() {
});
}
#Override
public JavaType getOutputType(TypeFactory typeFactory) {
return typeFactory.constructType(new TypeReference<String>() {
});
}
}
and now, you can use annotation like below:
#JsonDeserialize(contentConverter = JsonNode2StringConverter.class)
private List<String> companies;
Solutions 2. and 3. solve this problem almost in the same way - read node and convert it back to JSON, but uses different approaches.
If, you want to avoid deserialising and serialising process you can take a look on solution provided in this article: Deserializing JSON property as String with Jackson and take a look at:
How to serialize JSON with array field to object with String field?
How to get a part of JSON as a plain text using Jackson
How to extract part of the original text from JSON with Jackson?

Related

#JsonPoperty can not get nested value

I have:
{
"id": "2021-04-03T15-SV_Waldhof_Mannheim--Zwickau",
"something": {
"id": "12",
"value": 1.5
}
}
I want get value: 1.15, and store it in my variable.
How can i do it with #JsonPropety?
#JsonProperty("something[value}") //how to do it correctly?
private float value;
How i parse JSON:
restTemplate.exchange(MY_GET_REQUEST, HttpMethod.GET, entity, new ParameterizedTypeReference<List<MyEntity>>(){})
I will be grateful for any help, if you know identical topics - just send link
UPDATED
something.value does not work
The same problem with unpacking, such as:
#JsonProperty("something")
public void setLng(Map<String, Float> coordinates) {
this.value= (Float.parseFloat(coordinates.get("value")));
}
Also does not work
You have 2 options:
Use custom deserializer for your response. In this case you able to populate any target DTO in any way. Here you could find example of custom deserializer
Use the same structure for your DTO as in response (with sub object) and add additional method in root DTO to access this value. But in this case it could produce side effects on serialization (for example, additional field in root DTO)
UPDATE
Such configuration is working for me
public static class Obj {
#JsonProperty("id")
String id;
Float value;
#JsonProperty("something")
public void value(Map<String, Object> obj) {
this.value = Float.parseFloat(obj.get("value").toString());
}
}

Creating POJOs for json with single field containing Array with RestTemplate

I am creating a client for the following format of JSON -
{
"results": [
{
"Product": "K265113",
"Language": "EN",
"LongText": "FIXTURE,INTERIOR,WALL"
}
]
}
The JSON always contains "results" field which is an array of a single element (it will always be a single element in this array). I just need LongText field from the JSON and nothing else. I am using Spring RESTTemplate.
I know that it works if I create two DTOs like -
public class ParentDTO
{
private List<ChildDTO> results;
public List<ChildDTO> getResults()
{
return results;
}
public void setResults(List<ChildDTO> results)
{
this.results = results;
}
}
public class ChildDTO
{
private String longText;
public String getLongText()
{
return longText;
}
#JsonProperty("LongText")
public void setLongText(String longText)
{
this.longText = longText;
}
}
But is there any way to read longText by creating a single DTO as the parent DTO is not having any useful field as I know there will always but just one element in the results array.
The reason you need only single DTO could be that you want only single class to perform this task. You can achieve that using ChildDTO as inner class which will make it more readable and maintainable.
The other way is to not parse the spring template response into DTOs instead use JSONNode class of Jackson databind API.
JsonNode root = objectMapper.readTree(response.getBody());
You can find more information at
https://fasterxml.github.io/jackson-databind/javadoc/2.8/com/fasterxml/jackson/databind/JsonNode.html
You can traverse down the tree and could retrieve the value of the attribute directly without any DTOs.

Custom Deserialiser to parse complex Json

I have to deserialise below Json
{
"Student": [
{
"Number": "12345678",
"Name": "abc"
"Country": "IN",
"AreaOfInterest": [
{
“FootBall”: “Yes”,
“Cricket”: “No”
}
]
}
],
"hasMore": false,
"links": [
{
"rel": "self",
"kind": "collection"
}
]
}
into below POJO
class {
private String number;
private String name;
private String footBall;
}
I have written Gson custom deserialiser to lift up AreaOfInterest as below
public List<? extends Student> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
var jsonObject = json.getAsJsonObject();
Stream<JsonElement> student = StreamSupport.stream(jsonObject.getAsJsonArray("Student").spliterator(), true);
Stream<JsonElement> areaOfInterest = StreamSupport.stream(jsonObject.getAsJsonArray("Student").get(0).getAsJsonObject().get("AreaOfInterest").getAsJsonArray().spliterator(), true);
Stream.concat(student,areaOfInterest)
.map(it -> context.deserialize(it, Student.class))
.map(Student.class::cast)
.collect(List.collector())
}
But deserialiser returning two objects of Student instead of one, one is all fields are null except footBall other is actual student except footBall as null, any help how to get single object with all the fields will be of great help, thanks in advance.
This won't be your exact answer, but it might be simpler to use gson to obtain a map and construct your pojo from that map. Alternatively, if you don't like the map, create a pojo that looks like your JSON and map that pojo to the pojo you want.
Background/Reasoning: GSON is the mapper of your choice right now, but might be changed to something else, eg. Jackson, and all of your custom, framework specific mappers will need to be converted/changed if that happens. Using gson to create an object, that looks like the source, and map that to your custom POJO in your controller will make your codes intention clear and your code more resilient to framework changes.

Embedding a Jackson JsonNode in POJO stored in CrudRepository

Here's where I'm at. I've an MVC controller method that accepts JSON content. Because I need to validate it using JSON Schema, my controller maps the request body as a Jackson JsonNode.
Upon successful validation, I need to persist the data in Spring Couchbase repository. Consider the following snippet:
public class Foo
{
#Id
private String _id;
#Version
private Long _rev;
#Field
private JsonNode nodeData;
// .. Other data and members.
}
//
// Repository
//
#Repository
public interface FooRepository extends CrudRepository<Foo, String> {
}
When I store these elements into the Couch repository, what I'd like to see is something like this:
{
"_class": "Foo",
"field1": "field 1 data",
"nodeData" : {
"Some" : "additional data",
"from" : "JsonNode"
}
}
instead, what I see in the repository is something like this:
{
"_class": "Foo",
"field1": "field 1 data",
"nodeData" : {
"_children": {
"Some": {
"_value": "additional data",
"_class": "com.fasterxml.jackson.databind.node.TextNode"
},
"From": {
"_value": "jsonNode",
"_class": "com.fasterxml.jackson.databind.node.TextNode"
},
"_nodeFactory": {
"_cfgBigDecimalExact": false
}
}
}
Each stored property of the JsonNode is decorated with class information, and other meta-data, which is not desirable.
My question - is there a preferred way to get the CrudRepository to behave in the manner that I wish?
It doesn't work that way because serialization and de-serialization conventions are already established. You can override these conventions with custom serialization & de-serialization in Jackson-- but that might go beyond the "crude" approach you are looking for.
I see you want a one shoe fits all approach to data modeling.
Might i recommend storing a Map
#Field
private Map<String, String> data;
This map is private so its perfect.
You can then have two methods
one method puts to the map like so
ObjectMapper mapper = new ObjectMapper()
public void setFeild(String name, Object value) {
ObjectNode node new ObjectNode(JsonNodeFactory.instance);
node.put("clazz", value.getClass().getName());
if (value instance of String) {
node.put("value", value)
} else {
node.put("value", mapper.writeValueAsString(data));
}
data.put(name, node.toString());
}
the other gets from the map like so
public Object getField(String name) {
if (data.contains(name)) {
JsonNode node = mapper.readValue(data.get(name), JsonNode.class);
Class clazz = Class.forName(node.get("class").textValue());
if (clazz.equals(String.class) {
return node.get("value").textValue();
} else {
return (Object) mapper.readValue(node.get("value"), clazz);
}
}
}
You should update this implementation to handle Date, Integer, Boolean, Double ... etc the same way i am handling String-- POJOs are what you serialize/de-serialize to/from json.
I hope this makes sense.

Convert JSON String to Pretty Print JSON output using Jackson

This is the JSON string I have:
{"attributes":[{"nm":"ACCOUNT","lv":[{"v":{"Id":null,"State":null},"vt":"java.util.Map","cn":1}],"vt":"java.util.Map","status":"SUCCESS","lmd":13585},{"nm":"PROFILE","lv":[{"v":{"Party":null,"Ads":null},"vt":"java.util.Map","cn":2}],"vt":"java.util.Map","status":"SUCCESS","lmd":41962}]}
I need to convert the above JSON String into Pretty Print JSON Output (using Jackson), like below:
{
"attributes": [
{
"nm": "ACCOUNT",
"lv": [
{
"v": {
"Id": null,
"State": null
},
"vt": "java.util.Map",
"cn": 1
}
],
"vt": "java.util.Map",
"status": "SUCCESS",
"lmd": 13585
},
{
"nm": "PROFILE
"lv": [
{
"v": {
"Party": null,
"Ads": null
},
"vt": "java.util.Map",
"cn": 2
}
],
"vt": "java.util.Map",
"status": "SUCCESS",
"lmd": 41962
}
]
}
Can anyone provide me an example based on my example above? How to achieve this scenario? I know there are lot of examples, but I am not able to understand those properly. Any help will be appreciated with a simple example.
Updated:
Below is the code I am using:
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.defaultPrettyPrintingWriter().writeValueAsString(jsonString));
But this doesn't works with the way I needed the output as mentioned above.
Here's is the POJO I am using for the above JSON:
public class UrlInfo implements Serializable {
private List<Attributes> attribute;
}
class Attributes {
private String nm;
private List<ValueList> lv;
private String vt;
private String status;
private String lmd;
}
class ValueList {
private String vt;
private String cn;
private List<String> v;
}
Can anyone tell me whether I got the right POJO for the JSON or not?
Updated:
String result = restTemplate.getForObject(url.toString(), String.class);
ObjectMapper mapper = new ObjectMapper();
Object json = mapper.readValue(result, Object.class);
String indented = mapper.defaultPrettyPrintingWriter().writeValueAsString(json);
System.out.println(indented);//This print statement show correct way I need
model.addAttribute("response", (indented));
Below line prints out something like this:
System.out.println(indented);
{
"attributes" : [ {
"nm" : "ACCOUNT",
"error" : "null SYS00019CancellationException in CoreImpl fetchAttributes\n java.util.concurrent.CancellationException\n\tat java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:231)\n\tat java.util.concurrent.FutureTask.",
"status" : "ERROR"
} ]
}
which is the way I needed to be shown. But when I add it to model like this:
model.addAttribute("response", (indented));
And then shows it out in a resultform jsp page like below:
<fieldset>
<legend>Response:</legend>
<strong>${response}</strong><br />
</fieldset>
I get something like this:
{ "attributes" : [ { "nm" : "ACCOUNT", "error" : "null
SYS00019CancellationException in CoreImpl fetchAttributes\n
java.util.concurrent.CancellationException\n\tat
java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:231)\n\tat
java.util.concurrent.FutureTask.", "status" : "ERROR" } ] }
which I don't need. I needed the way it got printed out above. Can anyone tell me why it happened this way?
To indent any old JSON, just bind it as Object, like:
Object json = mapper.readValue(input, Object.class);
and then write it out with indentation:
String indented = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(json);
this avoids your having to define actual POJO to map data to.
Or you can use JsonNode (JSON Tree) as well.
The simplest and also the most compact solution (for v2.3.3):
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
mapper.writeValueAsString(obj)
The new way using Jackson 1.9+ is the following:
Object json = OBJECT_MAPPER.readValue(diffResponseJson, Object.class);
String indented = OBJECT_MAPPER.writerWithDefaultPrettyPrinter()
.writeValueAsString(json);
The output will be correctly formatted!
ObjectMapper.readTree() can do this in one line:
mapper.readTree(json).toPrettyString();
Since readTree produces a JsonNode, this should pretty much always produce equivalent pretty-formatted JSON, as it JsonNode is a direct tree representation of the underlying JSON string.
Prior to Jackson 2.10
The JsonNode.toPrettyString() method was added in Jackson 2.10. Prior to that, a second call to the ObjectMapper was needed to write the pretty formatted result:
mapper.writerWithDefaultPrettyPrinter()
.writeValueAsString(mapper.readTree(json));
For Jackson 1.9, We can use the following code for pretty print.
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.enable(SerializationConfig.Feature.INDENT_OUTPUT);
I think, this is the simplest technique to beautify the json data,
String indented = (new JSONObject(Response)).toString(4);
where Response is a String.
Simply pass the 4(indentSpaces) in toString() method.
Note: It works fine in the android without any library. But in java you have to use the org.json library.
You can achieve this using bellow ways:
1. Using Jackson
String formattedData=new ObjectMapper().writerWithDefaultPrettyPrinter()
.writeValueAsString(YOUR_JSON_OBJECT);
Import bellow class:
import com.fasterxml.jackson.databind.ObjectMapper;
It's gradle dependency is :
compile 'com.fasterxml.jackson.core:jackson-core:2.7.3'
compile 'com.fasterxml.jackson.core:jackson-annotations:2.7.3'
compile 'com.fasterxml.jackson.core:jackson-databind:2.7.3'
2. Using Gson from Google
String formattedData=new GsonBuilder().setPrettyPrinting()
.create().toJson(YOUR_OBJECT);
Import bellow class:
import com.google.gson.Gson;
It's gradle is:
compile 'com.google.code.gson:gson:2.8.2'
Here, you can also download correct updated version from repository.
This looks like it might be the answer to your question. It says it's using Spring, but I think that should still help you in your case. Let me inline the code here so it's more convenient:
import java.io.FileReader;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.ObjectWriter;
public class Foo
{
public static void main(String[] args) throws Exception
{
ObjectMapper mapper = new ObjectMapper();
MyClass myObject = mapper.readValue(new FileReader("input.json"), MyClass.class);
// this is Jackson 1.x API only:
ObjectWriter writer = mapper.defaultPrettyPrintingWriter();
// ***IMPORTANT!!!*** for Jackson 2.x use the line below instead of the one above:
// ObjectWriter writer = mapper.writer().withDefaultPrettyPrinter();
System.out.println(writer.writeValueAsString(myObject));
}
}
class MyClass
{
String one;
String[] two;
MyOtherClass three;
public String getOne() {return one;}
void setOne(String one) {this.one = one;}
public String[] getTwo() {return two;}
void setTwo(String[] two) {this.two = two;}
public MyOtherClass getThree() {return three;}
void setThree(MyOtherClass three) {this.three = three;}
}
class MyOtherClass
{
String four;
String[] five;
public String getFour() {return four;}
void setFour(String four) {this.four = four;}
public String[] getFive() {return five;}
void setFive(String[] five) {this.five = five;}
}
Since jackson-databind:2.10 JsonNode has the toPrettyString() method to easily format JSON:
objectMapper
.readTree("{}")
.toPrettyString()
;
From the docs:
public String toPrettyString()
Alternative to toString() that will
serialize this node using Jackson default pretty-printer.
Since:
2.10
If you format the string and return object like RestApiResponse<String>, you'll get unwanted characters like escaping etc: \n, \". Solution is to convert your JSON-string into Jackson JsonNode object and return RestApiResponse<JsonNode>:
ObjectMapper mapper = new ObjectMapper();
JsonNode tree = objectMapper.readTree(jsonString);
RestApiResponse<JsonNode> response = new RestApiResponse<>();
apiResponse.setData(tree);
return response;
Anyone using POJO, DDO, or response class for returning their JSON can use spring.jackson.serialization.indent-output=true in their property file. It auto-formats the response.

Categories

Resources