Deserialize OneOf in two differents classes Java 11 - java

My question is the following, I can receive a JSON, where inside it can come two types of class "CHANGE" or "WITHDRAW" in the same type of request. Below is an example JSON 1 and JSON 2
JSON 1
{
"name":"Eduard Jack",
"change":{
"agency":"3213"
}
}
JSON 2
{
"name":"john Stev",
"withdraw":{
"documentNumber":"121212"
}
}
When I make this request I get a string from json where I make a mapper to convert into an object, in the OPENAPI documentation it tells me that it is oneOf, it can be one or the other, never both together. How can I make the code understand which class to deserialize into in the Java 11?

I think there are multiple ways to do so
This will convert you string into JsonNode where JSON1 is the string provided
JsonNode json = mapper.readTree(JSON1);
if (json.get("withdraw") == null) : //the value of withdraw will be null in JSON1
JsonParser parser = mapper.treeAsTokens(json);
Change change= mapper.readValue(parser, Change.class);
else: //the value of withdraw wont be null in
JsonParser parser = mapper.treeAsTokens(json);
Withdraw withdraw= mapper.readValue(parser, withdraw.class);

Related

How get just one value from json as string

I have following json as string:
{
"field1":"value1",
"field2":"value2",
"field3":{
"field31":"value31",
"field32":"value32"
},
"field4":{
"field41":"value41"
}
}
What is the best and the most modern way to get from this json just value from field41, so I would return "value41". I know that I can use JSONObject but I'm wondering weather we have any other better option?
Try JSONPath
String json = "...";
String field41 = JsonPath.read(json, "$.field4.field41");
You can test it here - https://jsonpath.herokuapp.com/
If you want to generate a real object out of it you can use Gson. You need to describe the class first. There are online json to Java objects converters out there. And then you can just call:
YourObject obj = new Gson().fromJson(json,YourObject.class);
System.out.println(obj.getField4().getField41());
And there you have it!

Detecting object types in JSON document

I have a text file as input which contains Employees. Now I need to refactor the code and the JSON can either contain an Employee[] array or a Building[] array. I am using Jackson, and there are enums in my Java class.
{
Employee[]
}
{
Building[]
}
I have code to selectively parse each document, but how can the code detect whether it is an Employee doc or a Building doc?
I know it's kind of a bad design, but there are some constraints for which I am doing so.
I need something like this:
boolean isBuildingDoc(String json);
boolean isEmployeeDoc(String json);
How can I do this?
My advice would be to add a top level property to your json called 'info'.
For example:
{
"info": {
"document_type": "employee"
},
"content": {
// rest of your json goes here...
}
}
Put the old json you used to generate in 'content'. Then, when you parse your json file, you can easily check the info.document_type flag to see what document type you are working with.
However, if you do not have control over the json that is being parsed (e.g. if it is being sent from another program), then this approach will not work.
You can use tree modal of jackson to detect Employee or Building node.
E.g.
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree(jsonString);
if(node.get("Employee") != null){
//handle it according to employee
}
else if(node.get("Building ") != null){
//handle it according to building
}
Note : Do remember one thing, get method searches for field in child elements also, So if your Employee array contains Building field as a child element, then this method will not work.

JSON Processing using Jackson 2.2

I am using Jackson 2.2 to read a very large JSON string into a Java object. Using ObjectMapper, I read everything into the memory and transform the JSON string to object. This is all happening in memory, so the entire JSON string is loaded into the memory and transformed into the object.
If there a more memory efficient way of doing it? i.e. without loading the entire JSON string into the memory, I should still be able to load the object.
Yes, you typically use so-called "streaming" API to iterate over JSON tokens; and once positioned over first token of the value you want (START_OBJECT for JSON Objects), use data-binding API, passing reader/parser for it to use. Details of this depend on library. I know that at least following support this mode of operation:
Jackson
Gson
Genson
For Jackson, basic Streaming API usage is talked about here (for example); but one thing that does not show is how to bind objects once you are positioned at the right place.
So assuming JSON like:
{ "comment" : "...",
"values" : [
{ ... value object 1 ... },
{ ... value object 2. ... }
]
}
you could do:
ObjectMapper mapper = new ObjectMapper();
JsonParser jp = mapper.getFactory().createJsonParser(jsonInput);
jp.nextToken(); // will return START_OBJECT, may want to verify
while (jp.nextValue() != null) { // 'nextValue' skips FIELD_NAME token, if any
String fieldName = jp.getCurrentName();
if ("values".equals(fieldName)) {
// yes, should now point to START_ARRAY
while (jp.nextToken() == JsonToken.START_OBJECT) {
ValueObject v = mapper.readValue(jp, ValueObject.class);
// process individual value in whatever way to you want to...
}
} else if ("comment".equals(fieldName)) {
// handle comment?
} // may use another else to catch unknown fields, if any
}
jp.close();
and that should let you only bind one object at a time.

Can't parse JSON property "null"

I faced with one trouble when tried to parse JSON "null" property, please help me to understand what's the real problem. I had a following JSON:
{
"properties" : {
"null" : {
"value" : false
}
}
}
I used http://jsonlint.com to validate that this JSON is valid. I tried to parse it from java:
import net.sf.json.JSONObject;
import java.io.IOException;
public class Test {
public static void main(String[] args) throws IOException {
String st = "{" +
" 'properties' : {" +
" 'null' : {" +
" 'value' : false" +
" }" +
" }" +
"}";
JSONObject.fromObject(st);
}
}
But got the exception:
Exception in thread "main" java.lang.ClassCastException: JSON keys must be strings.
at net.sf.json.JSONObject._fromJSONObject(JSONObject.java:927)
at net.sf.json.JSONObject.fromObject(JSONObject.java:155)
at net.sf.json.JSONSerializer.toJSON(JSONSerializer.java:108)
at net.sf.json.AbstractJSON._processValue(AbstractJSON.java:238)
at net.sf.json.JSONObject._processValue(JSONObject.java:2655)
at net.sf.json.JSONObject.processValue(JSONObject.java:2721)
at net.sf.json.JSONObject.element(JSONObject.java:1786)
at net.sf.json.JSONObject._fromJSONTokener(JSONObject.java:1036)
at net.sf.json.JSONObject._fromString(JSONObject.java:1201)
at net.sf.json.JSONObject.fromObject(JSONObject.java:165)
at net.sf.json.JSONObject.fromObject(JSONObject.java:134)
I used json-lib-2.4-jdk15.jar from http://json-lib.sourceforge.net to parse it. Could anybody please clarify this? Why this library throws exception, but online validator said that it's valid JSON? It is a bug in the library or I made something wrong?
JSON-lib initially parses and populates a Java Map with the input JSON. Unfortunately, JSON-lib then checks whether every JSON object element name is a JSON null. It's null check is performed in the JSONNull.equals(Object) method. This method returns true for a "null" JSON string, which of course is not actually a JSON null value.
I recommend filing a bug with the JSON-lib project for this issue. The implementation of JSONNull.equals(Object) is flawed.
Unfortunately, it's not possible to handle this with a custom PropertyNameProcessor.
Options available for a more immediate solution include altering the JSON-lib code yourself, or switching libraries.
If you can switch libraries, I highly recommend Jackson. Following is an example of using it to deserialize the example JSON in the original question.
/*
{
"properties" : {
"null" : {
"value" : false
}
}
}
*/
String json = "{\"properties\":{\"null\":{\"value\":false}}}";
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> map = mapper.readValue(json, Map.class);
System.out.println(map);
// output: {properties={null={value=false}}}
Map<String, Object> propertiesMap = (Map) map.get("properties");
System.out.println(propertiesMap);
// output: {null={value=false}}
Map<String, Object> nullMap = (Map) propertiesMap.get("null");
System.out.println(nullMap);
// output: {value=false}
The first JSON posted is valid JSON: the JSON in the Java, however, is not valid -- only " is valid for the [required] key quote. From json.org:
A string is a sequence of zero or more Unicode characters, wrapped in double quotes, using backslash escapes....
However, that sounds like a bug, assuming it was not triggered by the invalid JSON fed to it (the library can do whatever it wants with invalid JSON)... one would have to look at the source (or bug reports / user experience) to say conclusively if this is indeed a "bug". I have added some suggestions of things to try below which may either show expected behavior or outline the cause/issue in further detail.
Consider this minimal test-case (with valid JSON):
String st = "{ \"null\": \"hello world!\" }";
This may also shed more light, depending on if the first item is "null" or null when extracted:
String st = "[ \"null\" ]";
Happy coding.
The gson library link is:
http://code.google.com/p/google-gson/
I normally usr gson to generate the josn string,so I found some example someone else posted in stackoverflow to parse json string with gson,see the link:
Converting JSON to Java
suggest you to use Gson,
and construct the json string using java Map and List,
then use Gson to output the Map or List object

How to handle unknown-type json response in java

I'm new to JSON.
I need to receive a response (in form of a String) from a server. That response can be an object like
{"a" : "value", "b" : "value2", ...}
if the request was successful, or a single string like
"ERROR"
on error.
Using org.json.JSONObject, how do I check which one has been returned?
EDIT
I think this could work, but I'm not sure if this is the right way to do it
if(JSONString.equals("\"ERROR\"") {
//handle error
} else {
//parse actual object
}
Where JSONString is a String containing the server response
Could this work?
The string "ERROR" is not valid JSON. Look at the JSONWriter API and you will see there is no way to produce a JSON string like "ERROR".
If you always want to treat the server response as json, you will need to have it return something like { "error" : true } or { error : false }. Your program will then be able to deserialize check the error field.
If you don't have control on the server response, then you will need to test String.equals("ERROR") before deserializing.
Since you can't make the third party service output valid json, before you do json parsing, just do a string comparison to see if the response is "error".
A quoted string is a valid json value in my opinion. The grammar at json.org does not define object or array as special topleve productions, rfc 4627 defines json-text as being an object or array, but a json value can also be a number, string, boolean or null.
From my reading of the org.json javadoc the following should work:
Object value = new JSONTokener(inputString).nextValue();
if (value instanceof String && ((String)value).equals("ERROR")) {
// handle error
} else if (value instanceof JSONObject) {
// handle response data
}
Using the tokener you are not affected by eventual additional whitespace in the response.

Categories