I would like to have different object structure created from json.
For example I would like to create json with inner object but when I deserialize it should be assigned to a field.
Example json :
{
"id": 1,
"innerData": {
"a": "foo",
"b": "bar"
}
}
I would like to deserialize it to object with two fields - int id and String b
I've ended up with writing custom deserializer.
Related
I have this java class as -
import lombok.Data;
import java.util.List;
#Data
public class Dummy {
private int id;
private String name;
}
I have this JSON as -
[
[
{
"id" : 1,
"name": "John"
},
{
"id" : 2,
"name": "Maxwell"
}
],
[
{
"id" : 3,
"name": "Bryan"
}
]
]
I am currently developing a framework and I would not be knowing what would be the type of the collection that JSON fits in.
I am currently using Jackson library to parse the JSON to Java object.
The main thing is I don't know the type at compile time.
I would like to fit the json into List<List<Dummy>> but I don't know the type before hand. Is there any way to pass the generic in runtime?
Object object = new ObjectMapper().readValue(json, Collection.class);
List<List<Dummy>> l = new ObjectMapper().readValue(json, new TypeReference<List<List<Dummy>>>(){});
In the first approach I am passing the Collection.class but it converts the json to List<LinkedHashmap<String, String>> which I don't want.
But, In the second approach I get the actual conversion but the problem is I don't know the type at compile time.
Can I anyhow inject the generic dynamically or any other approach I would tackle this?
Any suggestion/help would be very helpful for me :)
Using the below method i could get the "id" element(first element in first list) value. i need to get that element value but based on the condition of another element "gifts.nameEn"
Response response = request.post("/pop/transaction/inquiry");
JsonPath jsonPathEvaluator = response.jsonPath();
List<HashMap> activeCampaignsList = jsonPathEvaluator.getList("");
for(int i=0; i< activeCampaignsList.size(); i++)
{
offerId = activeCampaignsList.get(i).get("id").toString();
break;
}
And Here's the response snippet so you would be aware of what exactly i'm looking for:
[ { "**id**": 1222, "type": "WITHIN_TRANSACTION", "gifts": [ { "startDate": "2019-06-26", "endDate": "2019-06-26", "id": 26130, "nameAr": "abcd201957261140057", "nameEn": "*abcd201957261140057*",
RestAssured allows you use JsonPath which has a strong utility in JSON processing. Instead of working with paths, you can convert JSON to Java Objects and I will show you exactly how to do it.
So, your JSON starts with an array of elements. Each of the element is JSON Object. Each of the JSON Object contains nested JSON Array gifts with JSON Objects in it. Let me explain further.
We have to process the array first, the start of your JSON [. In order to do that we can create a class representing this Array like this:
public class ElementObject { //this name is irrelevant. You can call it whatever you want
public String id; //the names of the variables are relevant
public String type; //each of variable name corresponds to the type and name in the JSON
public List<GiftObject> gifts; //here we handle the nested JSON Array of JSON Objects
}
Now, we require an object to handle gifts. That will require another class:
public class GiftObject { //irrelevant name
public String startDate; //relevant name
public String endDate;
public String id;
public String nameAr;
public String nameEn;
}
Now, all we have to do is call 1 method from JsonPath to parse whole JSON into Java classes. Like this:
response.jsonPath().getObject("", ElementObject[].class);
That's mostly it. Starting from the top:
[
{
"id":1222,
"type":"WITHIN_TRANSACTION",
We have an JSON Array with JSON Objects. 1 JSON Object equals 1 instance of ElementObject class. All of the objects from the Array will be parsed into ElementObject[].class and JsonPath will create an instance using Java Reflection (You don't have to worry about that).
"gifts": [ { "startDate": "2019-06-26", "endDate": "2019-06-26", "id": 26130, "nameAr": "abcd201957261140057", "nameEn": "abcd201957261140057",
Each of the gifts will be parsed into new instance of GiftObject and storred in List<GiftObject> in ElementObject class.
All we have to do now is work on our Java classes.
To fully answer your question - you need ID value based on gifts.nameEn
for (ElementObject element : elements) {
for (GiftObject gift : element.gifts) {
if (gift.nameEn.equals("abcd201957261140057")) {
System.out.println(element.id);
}
}
}
The above for loop can be easily replaced with a method, or Java Streams. Whatever suits you the most.
Given the above code the results will be:
1222
Hope it helps!
EDIT:
If you get the Exception like this Cannot deserialize object because of no JSON deserializer found in classpath. Please put either Jackson (Databind) or Gson in the classpath. all you have to do is add jackson-databind to Maven. Rest Assured is able to use Gson or Jackson databind to transform JSON into Java classes. All you need is the dependency, and you are all set.
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.9</version>
</dependency>
I am using the annotation #JsonIdentityInfo to serialize / deserialize an object using it's id.
I implemented an ObjectIdResolver, that is working well.
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id", resolver = EntityIdResolver.class, scope = MyClass.class)
public class MyClass{....}
I found the documentation of this annotation here.
My problem is the statement: "In practice this is done by serializing the first instance as full object and object identity, and other references to the object as reference values."
Now my Rest-Endpoint return something similar to
{
"itemList": [
{
"id": 70110,
"myClass": {
"id": 3,
"name": "itemName",
"date": "2000-01-01"
},
},
{
"id": 70111,
"myClass": 3,
}
]
}
But this means, the the client has to deal with different data types. The client does know all the objects, so it does not matter if the serialization returns either allways the id only or the full object.
How do I prevent Jackson from using this behaviour?
Finally found the solution (at least for my problem) quite quickly:
Adding
#JsonIdentityReference(alwaysAsId = true)
to MyClass will allways seralize as
"myClass":3
I found this here
Given a class of the following format:
Class A
{
Collection(Item) x= new ArrayList(Item)();
Collection(Link) y= new ArrayList(Link)();
.
.
//You can add data to the Item and Link classes
}
where we have
Class Item {
int x;
getX();
setX();
}
and similarly
class Link {
..//get and set methods
}
If i create an instance: A obj= new A();
how would i serialize obj using ObjectMapper mapper? Would it still be the same? That is `mapper.writeValue(writer, obj);
I want the output format to be:
{
"x": []
"y": []
}
JSON serialization of collections usually have the collection name as a key present, so if you serialize A, the two collections should be serialized how you wish it to be.
TL;DR
basically, my problem is that i have a list of wrapper objects
{"stuff": [
{"foobar" : {someObjectOfTypeA}},
{"barfoo" : {someObjectOfTypeB}},
{"foobar" : {someObjectOfTypeA}}
]}
and the type of someObjectOfTypeX depends on the value of the key "foobar" or "barfoo". how can i deserialize this? (for now) serializing is not a problem.
long version
i don't know enough jackson to solve the following problem. i've tried, but i'm stuck.
the json structure i want to parse looks like this:
{
"id": "foobar",
"responses": [
{
"responseType1": {
"code": 0,
"foo": "bar"
}
},
{
"responseType2": {
"code": 1,
"bar": {"foo": ...}
}
},
{
"responseType1": {
"code": 1,
"foo": "foobar"
}
}
]
}
i tried to deserialize it using jacksons full data binding. my pojos are:
// pseudocode
// the outermost object
#JsonCreator
ServiceResponse(
#JsonProperty("id") String id,
#JsonProperty("responses") ArrayList<ResponseWrapper> responses)
// every response has a wrapper. the wrapper is an object with just one key and one value. the value is an object of a certain class (ResponseTypeX extends AResponse), and the exact ResponseType is identified by the key (the key isn't the class name though).
#JsonCreator
ResponseWrapper(AResponse keyDependsOnTypeOfAResponse ???)
// base class for all responseTypeX classes
// all subclasses of AResponse have a code and a complex payload object
#JsonCreator
AResponse (
#JsonProperty("code") int code)
// one response type
// here, the payload is just a string, in reality it's a deep structure, so i dont want to parse this manually
#JsonCreator
ResponseType1 extends AResponse (
#JsonProperty("code") int code,
#JsonProperty("foo") String foo)
// one response type
#JsonCreator
ResponseType2 extends AResponse (
#JsonProperty("code") int code,
#JsonProperty("bar") SomeOtherObject foo)
as you can see, responses is an array of wrapper objects; the "payload" class of the wrapper object is identified by the key (but the keys aren't a 1:1 match to class names). my ResponseTypeX's are limited, there are about 20 of them, so if i have to do a manual key:value type identification, i'm happy.
but is it possible to write a manual deserializer for the WrapperResponse object and continue deserializing its children with full data binding? if so, how?
i tried to just make the Wrapper accept all possible ResponseTypes as properties, hoping it would just nullify the "unset" ones, e.g.
#JsonCreator
ResponseWrapper(
#JsonProperty("responseKey1") ResponseType1 response1,
#JsonProperty("responseKey2") ResponseType2 response2,
#JsonProperty("responseKey3") ResponseType3 response3,
...)
but this failed, probably because all ResponseTypes are subclasses of AResponse and thus jackson gets confused.
Some custom deserialization processing is necessary. I'd recommend including a simple registry (map) of foobar/barfoo-to-type entries in the solution, much like the sixth example in my old blog post from May 25, 2011, "Deserialize JSON with Jackson into Polymorphic Types - A Complete Example".