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
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'm receiving some JSON from an OrientDB server that looks something like this:
{
...
"out": ...,
"in": ...,
...
}
Now these two fields out and in can be one of two types: String and my own custom object (let's call it a Record). For example, for one request I might receive this:
{
...
"out": "#17:0",
"in": {
...
},
...
}
For another I might get:
{
...
"out": {
...
},
"in": "#18:2",
...
}
And so on. Both might be Strings, both might be Records, one might be a String and the other a Record, et cetera et cetera. Now when I'm deserializing this kind of JSON using Moshi, I'd have two parameters out and in to hold the values of their respective keys; however, because these values aren't a fixed data type, that's easier said than done.
Creating multiple POJOs (or "POKO"s, I guess, because I'm using Kotlin) wouldn't work, because these objects can be found inside other JSON objects and stuff like that. I'd need a single object for which these parameters can take on a variable data type. So how would I do that?
Would I have to write a custom adapter in Moshi for serializing/deserializing these values? If so, how would I go about writing one that can assign a certain data type depending on the value of the parameter? Or is there some sort of Kotlin class/function/extension function I can find/write that can hold two possible data types?
If it's relevant, I'm also using Retrofit 2 + RxJava 2 to make my HTTP calls asynchronously, so if there's any data types or functions in these libraries that facilitates something like this, I'm all ears.
Even if anyone can only answer in Java that's okay, because I can convert the code myself. And if I'm missing something obvious, I apologize in advance.
You can do something like what I did in this answer: https://stackoverflow.com/a/65106419/3543610
Basically you'd create a sealed class to be the type of both your properties in and out. You'd also need to wrap the primitive string one into a type holding a String, so you can make it extend the sealed class, something like this:
sealed class YourType {
data class StringData(val value: String) : YourType()
#JsonClass(generateAdapter = true)
data class Record(
val prop1: String,
val prop2: Int
) : YourType()
}
Then your model that has these properties would look smth like:
#JsonClass(generateAdapter = true)
data class Model(
...
val in: YourType,
val out: YourType,
...
)
then finally you write your custom adapter for the YourType type:
class YourTypeCustomAdapter {
#FromJson
fun fromJson(jsonReader: JsonReader, delegate: JsonAdapter<Record>): YourType? {
return if (jsonReader.peek() == BEGIN_OBJECT) {
delegate.fromJson(jsonReader)
} else {
StringData(jsonReader.nextString())
}
}
#ToJson
fun toJson(jsonWriter: JsonWriter, yourType: YourType, delegate: JsonAdapter<Record>) {
when (yourType) {
is Record -> delegate.toJson(jsonWriter, yourType)
is StringData -> jsonWriter.value(yourType.value)
}
}
}
and register with Moshi:
private val moshi = Moshi.Builder()
.add(YourTypeCustomAdapter())
.build()
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".
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.