Which class of Jackson library should I use to parse JSON data? - java

My JSON data:
[
"Gi 1/1",
{
"Shutdown":true,
"Speed":"force10ModeFdx",
"AdvertiseDisabled":0,
"MediaType":"dual",
"FC":"off",
"MTU":9600,
"ExcessiveRestart":false,
"PFC":0
}
]
Controlller class method:
public #ResponseBody void setSwitchPortInterfaceInfo(#RequestBody JsonNode jsonNode)
throws JsonProcessingException, IOException { }
When calling this method only "Gi 1/1" got parsed.
Which class do I need to pass as argument to parse complete JSON object?

The JSON Data represent in the question is not present in correct format. We need to convert it into proper version than only parse the Same.
Ideal way of declaring JSON structure is:
[{"key":"value"},{"key":"value"},{"key":"value"}]

ObjectMapper objectMapper = new ObjectMapper();
String strToParse = getString(); // put the string you want to parse
JsonNode node = objectMapper.readValue(strToParse, JsonNode.class);
System.out.println(node.get(0));
System.out.println(node.get(1));

Related

How to serialize an object and read it into JsonNode?

I have a simple Java object that has a String property that contains serialized java objects:
#Data
private class Test {
int id;
String data;
}
The data is created with
test.setData(mapper.writeValueAsString(anyJavaObjectThatShouldBeSerialized));
Then I send the Test Object to another server, where I want to deserialize the object from the data property into a JSONNode:
JsonNode node = mapper.readTree(test.getData());
The problem is, that the result of readTree is a TextNode and not a JsonNode. Therefore I can not access the properties of the serialized data.
The problem might be that the serialized object is interpreted as a String because when I print it it has quotationsmarks around it:
"{\"id\":39,\"name\":\"Im a test value\"}"
How do I get a JsonNode from the String that contains serialized objects? Please note that the data can be ANY Java object, that's why I serialized it into a String.
Make a double deserialization - first deserialize the string payload to json, then deserialize the json to class, or JsonNode in your case.
public class Temp {
public static void main(String[] args) throws Exception {
String payload = //your string payload;
ObjectMapper mapper = new ObjectMapper();
String json = mapper.readValue(payload, String.class);
JsonNode node = mapper.readTree(json);
}
}
As you noted, the cause is that you are making double serialization - first when setting the data field, then again when sending data to the other server.
You can avoid this double serialization/deserialization by making data an object.
#Data
class Test {
int id;
Object data;
}
test.setData(anyJavaObjectThatShouldBeSerialized);
Like this it will be serialized like JsonObject.
{
"id": 39,
"name": "Im a test value"
}

JSON to POJO as Object class using jackson java

I'm trying to use DTO to JSON (Write in Json file) and JSON to DTO (Read from JSON file) as a common methods (Generic method to be used by different pojo write/read operations)
Inorder to use as common method, i'm using return type as object.
Below my code
public String dtoToJSON(String appName, Object obj) throws IOException {
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
String postJson = mapper.writeValueAsString(obj);
System.out.println(postJson);
// Save JSON string to file
FileOutputStream fileOutputStream = new FileOutputStream("post.json");
mapper.writeValue(fileOutputStream, obj);
fileOutputStream.close();
return appName;
}
public Object jsonToDto() throws IOException {
ObjectMapper mapper = new ObjectMapper();
// Read JSON file and convert to java object
InputStream fileInputStream = new FileInputStream("post.json");
Object obj = mapper.readValue(fileInputStream, Object.class);
fileInputStream.close();
return obj;
}
I'm able to run DTO to JSON (Write in Json file) successfully but when i try to run JSON to DTO (Read from JSON file) i get ClassCastException
My exception:
thread "main" java.lang.ClassCastException: Cannot cast java.util.LinkedHashMap to com.me.dto.Post
My main method
public static void main(String[] args) throws IOException {
Transform ts=new Transform();
Post post=(Post)ts.jsonToDto();
// print post object
System.out.println("Printing post details");
System.out.println(post.getId());
System.out.println(post.getTitle());
System.out.println(post.getDescription());
System.out.println(post.getContent());
System.out.println(post.getLastUpdatedAt());
System.out.println(post.getPostedAt());
}
}
Please let me know if i'm wrong.
Thanks in advance.
It says thread "main" java.lang.ClassCastException: Cannot cast java.util.LinkedHashMap to com.me.dto.Post, which means your ts.jsonToDto() returns a LinkedHashMap and cannot be cast to your DTO.
You can refer here to have more information.
The issue's coming from Jackson. When it doesn't have enough information on what class to deserialize to, it uses LinkedHashMap.
Since you're not informing Jackson of the element type of your ArrayList, it doesn't know that you want to deserialize into an ArrayList of Accounts. So it falls back to the default.
They also gave you solutions there.
If you debug the code ,you will see the code below in class
com.fasterxml.jackson.databind.deser.std.UntypedObjectDeserializer
....
switch (p.getCurrentTokenId()) {
case JsonTokenId.ID_START_OBJECT:
{
JsonToken t = p.nextToken();
if (t == JsonToken.END_OBJECT) {
return new LinkedHashMap<String,Object>(2);
}
}
case JsonTokenId.ID_FIELD_NAME:
return mapObject(p, ctxt);
....
From the above ,we can see ,if your Class is java.lang.Object ,it will perform the case JsonTokenId.ID_START_OBJECT,and return a LinkedHashMap as a result.

Spring Boot REST application with Jackson - how to handle arbitrary/unknown xml structures passed in?

I am working with a Spring Boot REST application. We are using jackson to handle deserialization of XML as well as JSON passed in the request body. An example of an expected request body looks like this:
<formInput><formNum>999999</formNum><documentData>Completely unknown data structure here!</documentData></formInput>
In the documentData element, we will have a structure that is completely arbitrary/unknown on the server side. We don't care about the structure, because we only want to pass the xml that is nested in documentData on to another service.
The POJO that we are trying to map the request body onto looks like this:
#JsonDeserialize(using=FormInputJsonDeserializer.class)
public class FormInput {
private String formNum
private String documentData
public String getFormNum() {
return formNum
}
public void setFormNum(String formNum) {
this.formNum = formNum
}
public String getDocumentData() {
return documentData;
}
public void setDocumentData(String documentData) {
this.documentData = documentData;
}
}
The custom JsonDeserializer that we are trying to write:
public class FormInputJsonDeserializer extends JsonDeserializer<FormInput> {
#Override
public FormInput deserialize(JsonParser parser, DeserializationContext context)
throws IOException, JsonProcessingException {
FormInput formInput = new FormInput();
String fieldName
JsonToken currentToken
while (parser.nextToken() != null) {
currentToken = parser.getCurrentToken()
if (currentToken.equals(JsonToken.END_OBJECT)) {
continue
}
fieldName = parser.getCurrentName()
// formNum handling not written yet
if ("documentData".equalsIgnoreCase(fieldName)) {
if (parser.getCurrentToken().equals(JsonToken.START_OBJECT)) {
// we are at the start of documentData, and we need to capture the
// entire documentData node as a String since we don't know
// its structure
JsonFactory jfactory = new JsonFactory()
StringWriter jsonStringWriter = new StringWriter()
JsonGenerator jGen = jfactory.createGenerator(jsonStringWriter)
jGen.copyCurrentStructure(parser) // points to END_OBJECT after copy
jGen.close()
String documentDataJsonStr = jsonStringWriter.getBuffer().toString()
println("documentDataJsonStr: " + documentDataJsonStr)
}
}
}
// rest of code omitted
}
}
As I say, if the request body is xml, ideally I'd like to just keep it formatted as xml and assign that to the documentData String property. However I came up with the above custom deserialization code by following some other examples on StackOverflow. This parsing code ends up converting documentData to a JSON formatted String. Since I didn't know how to pass through the raw XML and get it mapped to the String property, I thought I could just convert the JSON formatted String back to a XML formatted String. A problem arises when we pass in a XML structure like this:
<formInput><formNum>9322</formNum><documentData><repeatLevel><subForm1><GROSS_DISTR> 13,004.31</GROSS_DISTR><GROSS_DISTR> 13,004.31</GROSS_DISTR><GROSS_DISTR> 13,004.31</GROSS_DISTR></subForm1></repeatLevel><repeatLevel><subForm1><GROSS_DISTR> 38,681.37</GROSS_DISTR><GROSS_DISTR> 38,681.37</GROSS_DISTR><GROSS_DISTR> 38,681.37</GROSS_DISTR></subForm1></repeatLevel></documentData></formInput>
After documentData is parsed in the deserialize method, the println statement shows the parsed JSON String as:
{"repeatLevel":{"subForm1":{"GROSS_DISTR":" 13,004.31","GROSS_DISTR":" 13,004.31","GROSS_DISTR":" 13,004.31"}},"repeatLevel":{"subForm1":{"GROSS_DISTR":" 38,681.37","GROSS_DISTR":" 38,681.37","GROSS_DISTR":" 38,681.37"}}}
This is actually not strictly valid JSON, due to the duplicate keys. I would have hoped that these would have been converted to JSON arrays, but that is not the case. So, I am unable to turn around and use something like the JSON.org libraries (JsonObject and XML) to convert the JSON String back to XML format (get an exception with a "duplicate key" error).
Does anybody have any suggestions or strategies for handling our situation?
You could try to use a JSONObject, add the #JsonIgnoreProperties("documentData") tag and extract documentData seperately from the raw data using substring()

Jackson readTree() ignore null and empty values

I have seen a few other posts that are similar to this so I apologise in advance if this has already been asked. However I can't get any of the solutions to work for me.
I am obtaining a chunk of JSON from a web service which I would then like to stash in Amazon's DynamoDB. However the JSON coming back has some null and empty values in it, which DynamoDB doesn't like.
So I would to remove these values from the JSON. I am currently using Jackson for JSON deserialising, via the readTree() method, as follows:
String sitesDataJson = webService.getData();
// create the object mapper and instruct it to skip null values
ObjectMapper mapper = new ObjectMapper();
mapper = mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
JsonNode jsonObj = null;
try {
jsonObj = mapper.readTree(sitesDataJson);
} catch (IOException e) {
e.printStackTrace();
}
The JSON I'm trying to read is as follows:
{
"domain_override":"",
"id":"106949",
"category_override":null,
"content_topic":null,
"modified_date":"2013-12-04 20:31:50",
"market":{
"brand_labels":{
"op":"allow_all"
},
"domains":null,
"blocked_contentattributes":{
"6":true
},
"blocked_creativetypes":{
"11":true,
"10":true,
"5":true
},
"brands":{
"ids":{
"5953":true,
"4644":true,
"8418":true,
"25480":true,
"95":true,
"5650":true
},
"op":"block"
},
"blocked_languages":{
},
"allow_unbranded_buyers":"0"
},
"type":"site",
"status":"Active",
"account_id":"100766",
"deleted":"0",
"v":"2",
"delivery_medium":"WEB",
"delivery_medium_id":"2",
"content_type_id":null,
"notes":null,
"platform_id":null,
"autorefresh_settings":{
},
"created_date":"2013-10-16 16:49:48",
"external_id":null,
}
Performing the readTree() with the NON_EMPTY or NON_NULL setting appears to make no difference. Doing a print of jsonObj using .toString() shows the empty and null values still there.
So am I going about this in the wrong way? Is there a better (or just correct!) way to do this?
I should also add that I have tried to do the same but using GSON, which I read removes these attributes by default. However that is not removing them either. So does this point to some oddity in the JSON being used?
But then I can't see how it can be as I've tried testing my code (both Jackson and GSON) with a very simple bit of JSON:
String json = "{"
+ "\"name\" : \"val1\","
+ "\"address\" : null"
+ "}";
But this still does not remove the address attribute when readTree() is used. Does this imply that the NON_NULL and NON_EMPTY settings have no effect on the readTree() method? Are they only in fact for when objects are serialised to JSON? Do I really need to read my JSON in to a POJO to be able to handle null or empty values?
Many thanks.
I am not sure 100% if the solution I am proposing will work for your case, but there is a way to use custom deserializers during the deserialization process.
StringDeserializer deserializer = new StringDeserializer();
SimpleModule module = new SimpleModule("StringDeserializerModule",
new Version(1, 0, 0, null));
module.addDeserializer(String.class, deserializer);
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(module);
Now you can add more custom desirializers for the properties you think null/ empty can be present and customize the deserialize() representation.
class StringDeserializer extends JsonDeserializer<String>
{
#Override
public String deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException
{
//Customize
}
}

Parse JSON array with jackson where objects are strings?

I have a slightly odd question. I have created an object, let's call it a Profile, that successfully parses single JSON objects via an API that I call. There is also a multi-profile interface that will return a JSON array of Profile objects. The problem is, the multi-profile interface turns the sub objects into strings. Is there an automatic way I can tell jackson to parse these into objects?
Example of a single object:
{ "foo": "bar" }
Example of a multi object:
[ "{ \"foo\": \"bar\" }", "{ \"blah\": \"ugh\" }" ]
(Sorry can't use real data)
Notice that the sub objects are actually strings, with escaped quotes inside them.
For completeness, my code for the multi object parse looks like this:
ObjectMapper mapper = new ObjectMapper();
Profile[] profile_array = mapper.readValue(response.content, Profile[].class);
for (Profile p: profile_array)
{
String user = p.did;
profiles.put(user, p);
}
As I said, in the single-profile case, the Profile object parses. In the multi-profile case, I get this exception:
Exception: org.codehaus.jackson.map.JsonMappingException: Can not construct instance of com.xyz.id.profile.Profile, problem: no suitable creator method found to deserialize from JSON String
I suppose you'll have to create a custom deserializer and apply it to the every element of that array.
class MyCustomDeserializer extends JsonDeserializer<Profile> {
private static ObjectMapper om = new ObjectMapper();
#Override
public Profile deserialize(JsonParser jp, DeserializationContext ctxt) {
// this method is responsible for changing a single text node:
// "{ \"foo\": \"bar\" }"
// Into a Profile object
return om.readValue(jp.getText(), Profile.class);
}
}
There is no out-of-the-box support for "re-parsing" of embedded JSON-in-JSON content.
But this sounds like a possible request for enhancement (RFE)...
Have you tried using JAXB?
final ObjectMapper mapper = new ObjectMapper();
// Setting up support of JAXB
final AnnotationIntrospector introspector = new JaxbAnnotationIntrospector();
// make deserializer use JAXB annotations (only)
mapper.getDeserializationConfig().setAnnotationIntrospector(
introspector);
// make serializer use JAXB annotations (only)
mapper.getSerializationConfig().setAnnotationIntrospector(
introspector);
final StringReader stringReader = new StringReader(response);
respGetClasses = mapper.readValue(stringReader,
FooBarClass.class);
The above should get you started...
Also, you would need to mark each subclass like so:
#XmlElement(name = "event")
public List<Event> getEvents()
{
return this.events;
}

Categories