Converting JSON Array to Java Object when Array Elements Could Be Different - java

Could anyone offer suggestions on a JSON library or code snippet that deals with converting a JSON file such as the one below to a Java object when there could be different key-value pairs in each array element?
{
"tweets": [
{
"filter_level": "medium",
"retweeted_status": {
"contributors": null,
"text": "",
"entities": {
"symbols": [
],
"urls": [
],
"hashtags": [
],
"user_mentions": [
]
},
"more data":". . ."
"user": {
"user data":". . .",
. . .
},
},
"contributors": null,
"text": "",
"entities": {
"symbols": [
],
"urls": [
],
"hashtags": [
],
"user_mentions": [
{
"id": 32943506,
"name": "Jazzmen",
"indices": [
3,
17
],
"screen_name": "_PumpsAndJays",
"id_str": "32943506"
}
]
},
"more data":". . .",
"user": {
"user data":". . .",
. . .
},
},
{ //BEGIN NEW ELEMENT, As you can see this next element doesn't have the
"retweeted_status" key, so it's different from the element of the array before.
"filter_level": "medium",
"contributors": null,
"text": "",
"more data like the last element":". . .", . . .
I would like to convert a json object like this, made up of an array of "tweets" with information for each, but I cannot find any documentation on a case when the data differs slightly from element to element. In this specific case, an element may be a retweet or just a normal tweet. I would like to know if anyone has insight on how to do this conversion using a library like Jackson, Google Gson, or the like?
I have done a TON of research on this, read multiple tutorials, the documentation for multiple parser libraries and cannot find anything like this.

The quickest way to do this is to have the following class:
class Tweets {
public List<Map<String, Object> tweets;
}
Using Jackson and passing this class as the desired output will deserialize the JSON without errors, but you need to know the exact name of the fields for each tweet so that you can do .get("field_name") for them. You will also need to cast the value to what you know it to be: String, Integer, Map for nested objects, etc. This code will be hard to maintain.
Another option is to have a Tweet class that has all the possible values you expect from all variations on the tweet objects received. So add retweetedStatus even if it might not have it for certain objects in the array. Jackson will just leave the value as null if it's not present in the input.
class Tweets {
public List<Tweet> tweets;
}
class Tweet {
String filterLevel;
RetweetedStatus retweetedStatus;
String text;
// + any other fields you may receive and want to deserialize
}
// + any other classes the define nested objects like RetweetedStatus
You can even configure Jackson to ignore fields that are not mapped in your class if you want to ignore any fields in the json.
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
or by annotating the class
#JsonIgnoreProperties(ignoreUnknown = true)
public class MyDtoIgnoreUnknown { ... }
You can also use annotations to specify a different name for the variable than the one in the json:
#JsonProperty("retweeted_status")
public RetweetedStatus retweetedStatus
Notice the change from underscore separated to camelCase

Related

Check two JSON if match and generate report with deferent

I am trying to check if two JSON file is equal or not using java :
This is the first json
{
"filters": [
{
"name": "Data_Type",
"value": "Database"
},
{
"name": "Begin_Date",
"value": "2019-05-01"
},
{
"name": "End_Date",
"value": "2019-10-31"
}
]
}
and this is the secound one :
{
"filters": [
{
"name": "End_Date",
"value": "2019-10-31"
},
{
"name": "Begin_Date",
"value": "2019-05-01"
},
{
"name": "Data_Type",
"value": "Database"
}
]
}
I use this library zjsonpatch
This library is great but the issue for me I want to ignore the order for the array so In my two JSON file should match
Alo I don't need only check the match, also I need report with the defferant if exists as zjsonpatch provide
any suggestion ??
Your problem is relatively simple to solve. I won't write real code because you didn't specify what language you are using so you will get pseudocode.
create a method to determine if a json object is equal to another according to your rules
parse json a
place every individual item of json a into a collection
parse json b
place every individual item of json a into another collection
iterate over collection a removing item by item and trying to removing the current item from collection b
when a item from A can't be removed from B or if when you finish iterating collection B is not empty it means the jsons were different

How to extract a particular object type from json if the level is not pre-known?

I have a json like shown below (this is just representational):
The issue I am facing is that the Person object can be at different levels in the json. E.g. in below case it is at level 2 in case of RootNode1, at level 1 in case of RootNode2 and at level 0. Of course these levels are not limited to 2 and neither are they tied to RootNode in any way. (And these node values ar enot preknown. Only thing fixed and unique to identify Person object is "Type": "Person")
I have to extract Person object in all cases.
Is there a way to achieve this through traversal in JsonPath library : https://github.com/json-path/JsonPath?
[
{
"RootNode1": [
{
"ABC": [
{
"DEF": ""
},
{
"Name": "John",
"Type": "Person"
....
}
]
},
{
"DAC": {}
}
]
},
{
"RootNode2": [
{
"Name": "Williams",
"Type": "Person"
....
}
]
},
{
{
"Name": "Sam",
"Type": "Person"
....
}
}
]
Yes, it is completely possible.
If the readme is to be believed, he make
JsonPath.read(document, "$.RootNode1.ABC[1].Type");
For RootNode1
And
JsonPath.read(document, "$.RootNode2[1].Type");
For RootNode2
And
JsonPath.read(document, "$.Type");
it is the only way to do that I see. but there may be simpler, I'm used to using org.json personally
If I understand you correctly, then this jsonpath expression
$..Type
should output
[
"Person",
"Person",
"Person"
]
at whatever level Type is.
The correct way to do that is with the expression: $..[?(#.Type == 'Person')], like:
ptx.parse(json).read("$..[?(#.Type == 'Person')]", List.class)
// ptx is ParseContext object of com.jayway.jsonpath
With this I can get the List of persons without caring about the level the Person object was present.

Chosing appropriate TypeAdapter for changing field name in json

I have two possible different fields in json file that exclude each other. One of them is array of strings and the second one is array of tuples. I want to read those fields and make from it object one class. For example:
{
"optionA": [
"someName",
"randomName"
]
}
{
"optionB": [
{
"name": "someName",
"type": "typeA"
},
{
"name": "someName",
"type": "typeB"
}
]
}
object read from option has fixed type. Is there any way to handle this problem in GSON?

Gson Deserialization json which contains parameter that can be string or object

I have a Json like below.
{
"myItemArray": [
{
"id": "c8c1",
"price": 18,
"display": {
"inneritemName1": "innerItemValue1",
"inneritemName2": "innerItemValue2",
"inneritemName3": "innerItemValue3"
}
},
{
"id": "cac1",
"price": 2,
"display": "Lemonate"
}
]
}
As you can see that the item in my array has a parameter called "display" which can be String or Object. How can I deserialize this json using Gson?
I don't want to deserialize this string manually is there any other way to to this?
You can use Gson, but have to build a class, get each Json attribute into the relevent attribute in the Java class.
I highly recomand Jackson library, you only need to setup the Java class, and it takes care of the mapping and parsing.

How do I deserialize an array of JSON data into a POJO using an abstract class?

I am trying to map some JSON into a pojo. I've seen how to do it with using information in the actual data, but it my case I don't really have anything identifying how the data is different, but I do know what the list is made up of. In the example below, I could be getting a list of prisoners or buildings, but in this case I know I am getting a list of prisoners.
I am given the JSON below (I know the nested data is odd, but I am being given the JSON, I have no control over it.)
{
"data": [{
"data": {
"create_date": "2015-09-30",
"building": "12",
"dob": "2/11/1965",
"gender": "M",
"location": {
"zip": "10459"
},
"name": {
"first": "James",
"last": "Bond"
},
"id": "45"
},
"uri": "/prisoner/45"
}, {
"data": {
"create_date": "2015-09-27",
"building": "12",
"dob": "12/15/1985",
"gender": "M",
"location": {
"zip": "10459"
},
"name": {
"first": "Hans",
"last": "Gruber"
},
"id": "56"
},
"uri": "/prisoner/56"
}],
"totals": {
"total": 2,
"page": 1
},
"links": [{
"uri": "/prisoners?limit=2"
}]
}
I have a Data object that contains a totals object and a links object. I also have a Collection (in this case, a list) of a Datum object that contains the nested Data object array. The Datum object has a Thing abstract class along with a Uri string. I have a Prisoner and a Building class extending Thing. In the above JSON, I know I am getting a list of prisoners.
My current code:
ObjectMapper mapper = new ObjectMapper();
Data data = mapper.readValue(responseJSON.toString(), Data.class);
If I go into Datum and change the Thing object to the Prisoner object, it works perfectly. My question is how do I make it accept Prisoner or Building but still have the abstract Thing class in place, or at least make it so it can be more generic. I know I can make several Datum classes, but I believe there must be a way to have 1 Datum class. I actually have dozens of different possibilities of data I could get, not just the 2 in my example.
Do you mean, you know what the types of Thing will be at the time that you read the value? Because in that case, you could parameterise Data (i.e. Data<T extends Thing>), and propagate the type parameter to Datum to specify the subclass of Thing that you have. And then you need to tell Jackson about the parameterised type when you call readValue, which can be done statically like this:
mapper.readValue(jsonString, new TypeReference<Data<Prisoner>>(){});
It can be done dynamically by constructing a parameterised type using the TypeFactory available from the ObjectMapper. Something like:
mapper.readValue("", mapper.getTypeFactory()
.constructParametricType(Data.class, thingSubclass));

Categories

Resources