Include a list of name value pairs in Jackson object dynamically - jackson

In my response object, I can easily add a map that Jackson will serialise e.g.
public class Response {
String firstName;
String lastName;
Map map = ...
}
The JSON will be
{
"firstName": "tony",
"lastName": "lastName",
"map": {}
}
But say I don't want the word map and I want to dynamically add name value pairs so they are at the same level as firstName and lastName, how do I do that? Really don't want to extend Map.
Thanks

Related

Intellij SSR: how to extract structured information from initializer expressions

Is there a way to extract into some structured format (JSON, XML, whatever) partial information from a number of constructor calls expressions?
Say we have code like:
public enum Model {
TOYOTA,
FORD
}
public enum Dealership {
TOYOTA_GRAND_PRIX(Model.TOYOTA, Set.of("LAND-CRUISER", "AVENSIS")),
FORD_DELUXE(Model.FORD, Set.of("FOCUS"));
private final Model model;
private final Set<String> brands;
}
and I would like to get something like
[
{
"model": "TOYOTA",
"brands": ["LAND-CRUISER", "AVENSIS"]
},
{
"model": "FORD",
"brands": ["FOCUS"]
}
]
is there a way to achieve (something like) this, with SSR?

How to deserialize the string with nondeterministic keys using gson?

I have a JSON string in which the keys are not predictable.
The server returns the values with different keys with each response.
Sample JSON looks like -
{
"ids": {
"123": "08:10",
"456": "08:00"
}
}
Here, keys 123 and 345 are not fixed i.e. on the next request, my response would look as below -
{
"ids": {
"123": "08:10",
"456": "08:00"
}
}
Now, I want to parse this response into an object using GSON. So, I created the model classes as below -
data class SlotsResponse(
val ids: IDs
)
data class IDs(
val id: Map<String, String>
)
And in the code, I am trying to deserialize it as -
val response = Gson().fromJson(strResponse, SlotsResponse::class.java)
But, I am unable to get the values for IDs. They are null.
Can someone please help me to understand whatever I am trying to achieve is possible?
What you have represented with your current model contains one extra nested object. So it would represent JSONs that look like this:
{
"ids": {
"id": {
"123": "08:10",
"456": "08:00"
}
}
}
In your actual JSON, there is no field named id, so you only need the root object with the field ids, and the dynamic map:
data class SlotsResponse(
val ids: Map<String, String>
)

Populate data classes with values from linkedhashmap

I have a linkedhashmap that has the following shape: <String, Subject>. The class Subject has the following fields:
class Subject {
var name: Boolean? = null
var lastname: Boolean? = null
var location: Boolean? = null
..
}
final_result =
"admin" -> Subject
"customer" -> Subject
etc.
I need to populate data classes that have the following format:
data class SubjectSummary(
val admin: SubjectData,
val customer: SubjectData
...
)
data class SubjectData(val details: DetailsData)
data class DetailsData(val name:String, val lastName:String ...)
Because I need to serialize the SubjectSummary class and get the following json format:
{
"admin": {
"details": {
"name": "",
"lastname": "",
...
}
}
"customer": {
"details": {
"name": "",
"lastname": "",
...
}
}
}
How do I assign the final_result map to match the SubjectSummary structure? I have done it with simple data classes, but when the fields within the data class are data classes, I'm not sure hot to populate it. Any guidance?
For simplicity I'm only showing a small example with a few fields.
If your goal with this transformation is just to be able to serialize with the given JSON format, then you don't need this SubjectSummary class. A Map<String, SubjectData> would be sufficient and probably more convenient to create when transforming from the initial map.
Also, it seems that DetailsData contains the same fields as Subject. If that's the case there is no need for an extra class either.
So in the end it seems you just need to create a Map<String, SubjectData where SubjectData could be defined as data class SubjectData(val details: Subject). You can transform your initial map pretty easily then:
val transformed = finalResult.mapValues { (_, subject) -> SubjectData(subject) }

Pick any element from JSON schema while streaming data to parse

I'm writing a de-serializer which reads a huge json file and puts records matching a filter (logic in my application) into database. The json file has a fixed schema as follows:
{
"cityDetails": {
"name": "String",
"pinCodes": "Array of integers ",
"people": [{
"name": "String",
"age": "Integer"
}]
}
}
I am only interested in streaming list of "people" from the file. I am aware that GSON/Jackson provide streaming APIs which I can use but I want to avoid looping through the tokens as I stream them and match their name to see if I am interested in them. I believe that there should be a solution which can do the streaming in background and point/seek the stream to the token I am interested in. I don't see any reason why this should not be possible if I provide my JSON schema. Is there are solution available for this?
Here's a sample instance of my JSON:
{
"cityDetails": {
"name": "mumbai",
"pinCodes": ["400001", "400002"],
"people": [{
"name": "Foo",
"age": 1
}, {
"name": "Bar",
"age": 2
}]
}
}
With GSON I would just create corresponding DTOs for the data to be parsed.
So you have some wrapper that is the root object:
#Getter
public class Wrapper {
private CityDetails cityDetails;
}
and city details:
#Getter
public class CityDetails {
private List<Person> people;
}
and possibly many Persons in the list people:
#Getter
#ToString
public class Person {
private String name;
private Integer age;
}
Then you can simply use for example Reader like below:
#Test
public void test() {
Gson gson = new Gson();
// assuming your json is named "test.json" in the same directory as test
Reader r = new InputStreamReader(getClass().getResourceAsStream("test.json"));
Wrapper wrapper = gson.fromJson(r, Wrapper.class);
wrapper.getCityDetails().getPeople().forEach(p -> log.info("{}", p.toString()));
}
Gson will search and instantiate only what is specified in DTO-classes the rest is ignored when parsing.
A nice way of doing this would be to use JsonPath.
A json path of:
$.cityDetails.people
will return just the contents of the people array:
[
[
{
"name": "Foo",
"age": 1
},
{
"name": "Bar",
"age": 2
}
]
]
Here is a Java implementation...

jsog-jackson: Serializing object graphs

I am trying to serialize an object graph with parent/child references, essentially I have an entity that looks like this:
#Entity (name = "Container")
#JsonIdentityInfo(generator=JSOGGenerator.class)
public class Container {
public String type = "parent";
#JsonManagedReference ("child")
#OneToMany (mappedBy = "parent", cascade = CascadeType.PERSIST)
public List<Child> children;
}
#Entity (name = "Child")
#JsonIdentityInfo(generator=JSOGGenerator.class)
public class Child {
public String type = "child";
#JsonBackReference ("child")
#ManyToOne
public Parent parent;
}
when I try to serialize this to the client, then this is what I get:
{
"type": "parent",
#id: 1
"children": [
{
"type": "child",
#id: 2
},
{ ... }
]
}
I see #id properties on all objects but there is no sight of any #ref properties. If I have understood jsog and jsog-jackson right, then this is what should actually be serialized:
{
"type": "parent",
#id: 1
"children": [
{
"type": "child",
#id: 2
#ref: 1
},
{ ... }
]
}
What I would really like to have is a way of restoring the original back reference to the parent after restoring the serialized JSOG in the browser, so that instead of #ref I get the parent property in each child object back.
Your using two conflicting approaches to managing circular relationships. You can either use the JSOGGenerator OR the #JsonManagedReference and #JsonBackReference annotations.
The JSOGGenerator will include the #id and #ref properties in the JSON serialized format which is useful for deserializing the object in another language such as JavaScript.
The #JsonManagedReference and #JsonBackReference use the Java class information to identify the circular reference and subsequently that information is excluded from the JSON serialized format so another language like JavaScript can't deserialize the object because the required information is missing.
Another benefit to the JSOGGenerator is that it can handle deeply nested circular relationships as opposed to a limited parent-child relationship.