Jackson ObjectMapper Not Reading Inner Objects - java

I have a JSON file
{
"readServiceAuthorizationResponse": {
"serviceAuthorization": {
"serviceAuthorizationId": "50043~220106065198",
"status": "Approved",
"receivedDate": "2022-1-6 1:21:12 PM",
"providerFirstName": "Ranga",
"providerLastName": "Thalluri",
"organizationName": "General Hospital",
"serviceLines": [{
"statusReason": "Approved",
"procedureDescription": "Room & board ward general classification",
"requestedQuantity": "1.00",
"approvedQuantity": "1.00",
"deniedQuantity": "",
"quantityUnitOfMeasure": "Day(s)",
"providers": [{
"providerFirstName": "Ranga",
"providerLastName": "Thalluri",
"organizationName": ""
}]
}]
}
}
}
My Java to read this into an object is this:
package com.shawn.dto;
import java.nio.file.Files;
import java.nio.file.Paths;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.ObjectMapper;
#JsonIgnoreProperties(ignoreUnknown = true)
public class ServiceAuthorizationDTO {
public String serviceAuthorizationId;
public String status;
public String receivedDate;
public String providerFirstName;
public String providerLastName;
public String organizationName;
public ServiceLine[] serviceLines;
public static ServiceAuthorizationDTO create(String json) {
ObjectMapper m = new ObjectMapper();
try {
Outer outer = m.readValue(json, Outer.class);
return outer.readServiceAuthorizationResponse.serviceAuthorization;
} catch (Exception e) {
return null;
}
}
#JsonIgnoreProperties(ignoreUnknown = true)
static class ReadServiceAuthorizationResponse {
public ServiceAuthorizationDTO serviceAuthorization;
}
#JsonIgnoreProperties(ignoreUnknown = true)
static class Outer {
public ReadServiceAuthorizationResponse readServiceAuthorizationResponse;
}
#JsonIgnoreProperties(ignoreUnknown = true)
public static class ServiceLine {
String statusReason;
String procedureDescription;
String requestedQuantity;
String approvedQuantity;
String deniedQuantity;
String quantityUnitOfMeasure;
Provider[] providers;
}
#JsonIgnoreProperties(ignoreUnknown = true)
public static class Provider {
String providerFirstName;
String providerLastName;
String organizationName;
}
public static void main(String[] args) {
try {
String json = new String(Files.readAllBytes(Paths.get("c:/temp/test.json")));
ServiceAuthorizationDTO dao = ServiceAuthorizationDTO.create(json);
System.out.println("serviceAuthorizationId: " + dao.serviceAuthorizationId);
System.out.println("serviceLines[0].procedureDescription: " + dao.serviceLines[0].procedureDescription);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
When I run it I get this:
serviceAuthorizationId: 50043~220106065198
serviceLines[0].procedureDescription: null
The outer fields in the object like providerId are read from the JSON. But the serviceLines array shows 1 element, and all fields in that class are empty.
Any ideas? This is the first time I've used real objects with JSON. I've always mapped it into Map objects and pulled the fields out manually. Thanks.

Fields in classes ServiceLine and Provider have package-private access modifiers. Jackson can't deserialize into private fields with its default settings. Because it needs getter or setter methods.
Solution 1: Make fields public
public static class ServiceLine {
public String statusReason;
public String procedureDescription;
public String requestedQuantity;
public String approvedQuantity;
public String deniedQuantity;
public String quantityUnitOfMeasure;
public Provider[] providers;
}
Solution 2: Use #JsonAutoDetect annotation
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
public class ServiceLine {
String statusReason;
String procedureDescription;
String requestedQuantity;
String approvedQuantity;
String deniedQuantity;
String quantityUnitOfMeasure;
Provider[] providers;
}
Solution 3: Change visibility on the ObjectMapper (doc)
public static ServiceAuthorizationDTO create(String json) {
ObjectMapper objectMapper = new ObjectMapper();
try {
objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
Outer outer = objectMapper.readValue(json, Outer.class);
return outer.readServiceAuthorizationResponse.serviceAuthorization;
} catch (Exception e) {
return null;
}
}

Related

Mapping JSON response to different types

I'm using Spring 2.6 and we make a GET request via
restTemplate.exchange(url, HttpMethod.GET, httpEntity, ResponseType.class).getBody();
The JSON response can be of two kinds:
1st:
public class ResponseType {
private String data;
}
2nd:
public class ResponseType {
private Subclass data;
}
public class Subclass {
private String classId;
private String detail;
}
In the first version I only get a reference link to the subclass resource.
If the URL contains a 'resolve' flag, than the reference link get expanded already in the first request.
The classId then also specifies what kind of class it is ( 'a.b.c' or 'x.y.z' )
No problem for JSON, but how can I get a mapping in Java?
When having more fields being dynamic (link or instance based on classId) a manual way would be difficult to implement if the combination could be 2 links and 3 objects.
It also could be that a object has the same feature - a filed with a link or a instance of a class specified by classId.
The JSON response would be this:
{
"data": "abskasdkjhkjsahfkajdf-linkToResource"
}
or this:
{
"data": {
"classId": "a.b.subclass",
"detail": "some data"
}
}
or this:
{
"data": {
"classId": "a.b.subclass",
"detail": "some data"
"data2": "some-link-id",
"data3": {
"detailB": "foo",
"detailC": "some-link-id"
}
}
}
Here I do have a possible solution for my problem. The logic to print the address only or the POJO relies soley in the CustomItemSerializer. So it is possible to use this without using duplicate code in controllers.
package com.allianz.clana.datamodel.http.epc.test;
import java.io.IOException;
import java.text.ParseException;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import lombok.Data;
import lombok.NoArgsConstructor;
public class JacksonTester2 {
public static void main(String args[]) throws ParseException, JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
Item item2 = new Item("link");
Stuff stuff = new Stuff();
stuff.setItem(item2);
stuff.setFoo("foo");
String jsonStringStuff = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(stuff);
System.out.println(jsonStringStuff);
Item item3 = new Item("{ \"name\":\"ID3\", \"creationDate\":\"1984-12-30\", \"rollNo\": 1 }");
stuff.setItem(item3);
stuff.setFoo("bar");
jsonStringStuff = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(stuff);
System.out.println(jsonStringStuff);
}
}
class CustomItemSerializer extends StdSerializer<Item> {
private static final long serialVersionUID = 1L;
public CustomItemSerializer() {
this(null);
}
public CustomItemSerializer(Class<Item> t) {
super(t);
}
#Override
public void serialize(Item item, JsonGenerator generator, SerializerProvider arg2) throws IOException {
if (item != null) {
if (item.getItem() != null) {
System.out.println("ItemA POJO data");
generator.writePOJO(item.getItem());
} else {
System.out.println("raw data with link");
generator.writeString(item.getRawdata());
}
}
}
}
#Data
class Stuff {
Item item;
String foo;
}
#JsonSerialize(using = CustomItemSerializer.class)
#Data
#NoArgsConstructor
class Item {
private String rawdata;
#JsonIgnore
private ItemA item;
public Item(String rawdata) {
this.rawdata = rawdata;
if (rawdata.contains("{")) {
try {
this.item = new ObjectMapper().readerFor(ItemA.class).readValue(rawdata);
} catch (JsonMappingException e) {
e.printStackTrace();
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
}
}
#Data
#NoArgsConstructor
class ItemA{
private String name;
private int rollNo;
private String creationDate;
public ItemA(String name, int rollNo, String dob) {
this.name = name;
this.rollNo = rollNo;
this.creationDate = dob;
}
}
The output looks like this:
raw data with link
{
"item" : "link",
"foo" : "foo"
}
ItemA POJO data
{
"item" : {
"name" : "ID3",
"rollNo" : 1,
"creationDate" : "1984-12-30"
},
"foo" : "bar"
}
The CustomItemSerializer decides if the link is printed or the POJO.

XMl to JSON through java pojo using jackson

I am trying to convert xml to json. First I created java class using the following xml
<CompositeResponse>
<CompositeIndividualResponse>
<PersonIdentification>2222</PersonIdentification>
</CompositeIndividualResponse>
</CompositeResponse>
The following java classes are following:
public class Main {
public CompositeResponse CompositeResponse;
public CompositeResponse getCompositeResponse() {
return CompositeResponse;
}
public void setCompositeResponse(CompositeResponse CompositeResponse) {
this.CompositeResponse = CompositeResponse;
}
}
public class CompositeResponse {
private List<CompositeIndividualResponse> CompositeIndividualResponse;
public List<CompositeIndividualResponse> getCompositeIndividualResponse() {
return CompositeIndividualResponse;
}
public void setCompositeIndividualResponse(List<CompositeIndividualResponse> CompositeIndividualResponse) {
CompositeIndividualResponse = CompositeIndividualResponse;
}
}
public class CompositeIndividualResponse {
private String Persondentification;
public String getPersondentification() {
return Persondentification;
}
public void setPersonIdentification (String PersonIdentification) {
this.PersonIdentification = PersonIdentification;
}
}
I am using the following code for conversion:
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
public class XMLToJson {
public static void main(String[] args) throws IOException {
String content = new String(Files.readAllBytes(Paths.get("test.xml")));
XmlMapper xmlMapper = new XmlMapper();
Main poppy = xmlMapper.readValue(content, Main.class);
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(poppy);
System.out.println(json);
}
}
But i am getting the following exception that CompositeIndividualResponse is not recognized.
Exception in thread "main" com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "CompositeIndividualResponse" (class com.test.custom.copy.Main), not marked as ignorable (2 known properties: "CompositeResponse", "compositeResponse"])
at [Source: (StringReader); line: 3, column: 32] (through reference chain: com.test.custom.copy.Main["CompositeIndividualResponse"])
I believe my java pojo is not fit with xml data. So how to define collection of pojo to solve this problem so I can get the following json:
{
"CompositeResponse":{
"CompositeIndividualResponse":
[
{
"PersonSSNIdentification":"221212501"
}
]
}
}
Define you POJO like this,
public class CompositeResponse {
private List<CompositeIndividualResponse> compositeIndividualResponse;
public List<CompositeIndividualResponse> getCompositeIndividualResponse() {
return compositeIndividualResponse;
}
public void setCompositeIndividualResponse(List<CompositeIndividualResponse> compositeIndividualResponse) {
CompositeIndividualResponse = compositeIndividualResponse;
}
}
public class CompositeIndividualResponse {
private String personIdentification;
public String getPersonIdentification() {
return personIdentification;
}
public void setPersonIdentification (String personIdentification) {
this.personIdentification= personIdentification;
}
}
Then update your main program as below,
public class XMLToJson {
public static void main(String[] args) throws IOException {
String content = new String(Files.readAllBytes(Paths.get("test.xml")));
XmlMapper xmlMapper = new XmlMapper();
CompositeResponse poppy = xmlMapper.readValue(content, CompositeResponse.class);
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(poppy);
System.out.println(json);
}
}
Problem in field naming, all fields by default starts with small letter.
for example: compositeResponse
To avoid this problem add annotation #JsonProperty for each field, like this:
#JsonProperty("persondentification")
private String Persondentification;
The solution is I do not need the Main.java class. Also I need to add jackson annotation to define xml element. The working code is following.
CompositeResponse.java
public class CompositeResponse {
#JacksonXmlProperty(localName = "CompositeIndividualResponse")
#JacksonXmlElementWrapper(localName = "CompositeIndividualResponse",useWrapping = false)
private List<CompositeIndividualResponse> compositeIndividualResponse;
public List<CompositeIndividualResponse> getCompositeIndividualResponse() {
return compositeIndividualResponse;
}
public void setCompositeIndividualResponse(List<CompositeIndividualResponse> compositeIndividualResponse) {
this.compositeIndividualResponse = compositeIndividualResponse;
}
}
CompositeIndividualResponse.java:
public class CompositeIndividualResponse {
#JacksonXmlProperty(localName = "PersonIdentification")
private String personIdentification;
public String getPersonIdentification() {
return personIdentification;
}
public void setPersonIdentification (String personIdentification) {
this.personIdentification= personIdentification;
}
}
XMLToJson.java
public class XMLToJson {
public static void main(String[] args) throws IOException {
String content = new String(Files.readAllBytes(Paths.get("test.xml")));
XmlMapper xmlMapper = new XmlMapper();
CompositeResponse poppy = xmlMapper.readValue(content, CompositeResponse.class);
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(poppy);
System.out.println(json);
}
}

Changing name while assigning and getting value from and to json - Objectmapper java

While assigning values from DTO to different entity names, I assigned correctly with the help of JsonProperty. FrontEnd expecting in a different name. Values for DTO object will get from different object. That I have to assign to entity. Instead of using plain java and copying, am using objectmapper. Here then entity values will be used by frontend. How to print the entity values in different name? Please check below code.
//DTO Class
import java.util.List;
public class StaffDTO {
private String nameDT;
private List<String> skillDT;
//Getter and Setters
}
//Entity Class
import java.util.List;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"nameDT",
"skillDT"
})
public class Staff {
#JsonProperty("nameDT")
private String name;
#JsonProperty("skillDT")
private List<String> skills;
//Getter and Setters
}
//Call Method
import java.util.ArrayList;
import java.util.List;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
public class SeatMapCall1 {
public static void main(String[] args) {
try {
StaffDTO staffDTO = createDummyObject();
System.out.println(convertObjectToJson(staffDTO));
Staff staff= convertJsonToObject(convertObjectToJson(staffDTO),Staff.class);
System.out.println(convertObjectToJson(staff));
} catch (Throwable e) {
e.printStackTrace();
}
}
private static <T> T convertJsonToObject(String jsonStrRes,
Class<T> classArg) {
T resObj = null;
try {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
resObj = mapper.readValue(jsonStrRes, classArg);
} catch (Throwable e) {
e.printStackTrace();
}
return resObj;
}
public static <T> String convertObjectToJson(T obj) {
String jsonStringReq = null;
ObjectMapper objMapper = new ObjectMapper();
try {
jsonStringReq = objMapper.writeValueAsString(obj);
} catch (Exception e) {
}
return jsonStringReq;
}
private static StaffDTO createDummyObject() {
StaffDTO staffDTO = new StaffDTO();
staffDTO.setNameDT("mkyong");
List<String> skills = new ArrayList<>();
skills.add("java");
skills.add("python");
staffDTO.setSkillDT(skills);
return staffDTO;
}
}
//Displays output as
{"nameDT":"mkyong","skillDT":["java","python"]}
{"nameDT":"mkyong","skillDT":["java","python"]}
But I want
{"nameDT":"mkyong","skillDT":["java","python"]}
{"name":"mkyong","skills":["java","python"]}
If I use the below getter and setters in Staff class, I am getting expected as below
{"nameDT":"mkyong","skillDT":["java","python"]}
{"nameDT":"mkyong","skillDT":["java","python"],"name":"mkyong","skills":["java","python"]}
Here it includes both nameDT, skillDT and name, skills. I don't need nameDT, skillDT.
#JsonProperty("nameDT")
private String nameDT;
#JsonProperty("skillDT")
private List<String> skillDT;
public String getName()
{ return nameDT; }
public void setName(String nameDT)
{ this.nameDT = nameDT; }
public List<String> getSkill()
{ return skillDT; }
public void setSkill(List<String> skillDT)
{ this.skillDT = skillDT; }

Jackson - parse different model under same key at runtime

I have a specific json response from server, where under a key the content would be of different models also at a time only one of the model data would be present under the key.
While parsing the response into POJO how can I specify object type at runtime based on other field of contentType on same model.
Following is the code for better understanding of scenario.
Here content_type is type A and so under "content" key there would be model for object of class TypeA
"scheduled_content": {
"some_field": "value",
"content_type": "typeA",
"content" : {
"some_field" : "value"
"more_feilds" : "value"
}
}
Here content_type is type B and so under "content" key there would be model for object of class TypeB
"scheduled_content": {
"some_field": "value",
"content_type": "typeB",
"content" : {
"some_field_b" : "value"
"more_fields_for_b" : "value"
}
}
How can I write POJO classes to parse such json response?
The type classes are completely different models they don't have any field in common.
I believe that what you are looking for is called, in Jackson JSON terms, polymorphic deserialization by property name.
Here is how I do it with Jackson 2.1.4:
First create an abstract class ScheduledContent with common members and an abstract method that would operate on the content. Use the JsonTypeInfo annotation to mark the JSON property that would resolve the specific implementation and the JsonSubTypes annotation to register the subtypes by the values of the property previously specified:
import com.fasterxml.jackson.annotation.JsonSetter;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "content_type")
#JsonSubTypes({
#JsonSubTypes.Type(name = "typeA", value = ScheduledAContent.class),
#JsonSubTypes.Type(name = "typeB", value = ScheduledBContent.class)
})
public abstract class ScheduledContent {
private String someField;
#JsonSetter("some_field")
public void setSomeField(String someField) {
this.someField = someField;
}
public abstract void doSomethingWithContent();
}
The subtypes registration can also be done on the ObjectMapper as you will see later.
Then add the specific implementation for the ScheduledAContent class:
public class ScheduledAContent extends ScheduledContent {
private TypeAContent content;
public void setContent(TypeAContent content) {
this.content = content;
}
#Override
public void doSomethingWithContent() {
System.out.println("someField: " + content.getSomeField());
System.out.println("anotherField: " + content.getAnotherField());
}
}
with TypeAContent being:
import com.fasterxml.jackson.annotation.JsonSetter;
public class TypeAContent {
private String someField;
private String anotherField;
#JsonSetter("some_field")
public void setSomeField(String someField) {
this.someField = someField;
}
public String getSomeField() {
return someField;
}
#JsonSetter("another_field")
public void setAnotherField(String anotherField) {
this.anotherField = anotherField;
}
public String getAnotherField() {
return anotherField;
}
}
and also for the ScheduledBContent class:
public class ScheduledBContent extends ScheduledContent {
private TypeBContent content;
public void setContent(TypeBContent content) {
this.content = content;
}
#Override
public void doSomethingWithContent() {
System.out.println("someField: " + content.getSomeField());
System.out.println("anotherField: " + content.getAnotherField());
}
}
with TypeBContent being:
import com.fasterxml.jackson.annotation.JsonSetter;
public class TypeBContent {
private String someField;
private String anotherField;
#JsonSetter("some_field_b")
public void setSomeField(String someField) {
this.someField = someField;
}
public String getSomeField() {
return someField;
}
#JsonSetter("another_field_b")
public void setAnotherField(String anotherField) {
this.anotherField = anotherField;
}
public String getAnotherField() {
return anotherField;
}
}
And a simple Test class:
import java.io.IOException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.NamedType;
public class Test {
public static void main(String[] args) {
String jsonA = "{" +
"\"some_field\": \"main_some_field1\"," +
"\"content_type\": \"typeA\"," +
"\"content\" : {" +
" \"some_field\" : \"content_some_field\"," +
" \"another_field\" : \"content_another_field\"" +
"}}";
String jsonB = "{" +
"\"some_field\": \"main_some_field2\"," +
"\"content_type\": \"typeB\"," +
"\"content\" : {" +
" \"some_field_b\" : \"content_some_field_b\"," +
" \"another_field_b\" : \"content_another_field_b\"" +
"}}";
ObjectMapper mapper = new ObjectMapper();
/*
* This is another way to register the subTypes if you want to do it dynamically without the use of the
* JsonSubTypes annotation in the ScheduledContent class
*/
// mapper.registerSubtypes(new NamedType(ScheduledAContent.class, "typeA"));
// mapper.registerSubtypes(new NamedType(ScheduledBContent.class, "typeB"));
try {
ScheduledContent scheduledAContent = mapper.readValue(jsonA, ScheduledContent.class);
scheduledAContent.doSomethingWithContent();
ScheduledContent scheduledBContent = mapper.readValue(jsonB, ScheduledContent.class);
scheduledBContent.doSomethingWithContent();
} catch (IOException e) {
e.printStackTrace();
}
}
}
that will produce the output:
someField: content_some_field
anotherField: content_another_field
someField: content_some_field_b
anotherField: content_another_field_b
Using #JsonSetter in the setter methods may help. But in this case you will need to create setter methods for each type of fields in "content".
#JsonSetter("some_field")
public void setSomeField1(String field1) {
this.field1 = field1;
}
#JsonSetter("some_field_b")
public void setSomeField2(String field2) {
this.field1 = field1;
}

Jackson Serialization for subclasses

In the below example, I have a primary class - A and its subclass - B. Both can be used as a property in the general class X.
public class A
{
#JsonProperty("primary_key")
public final String primaryKey;
#JsonCreator
A(#JsonProperty("primary_key") String primaryKey)
{
this.primaryKey = primaryKey;
}
}
public class B extends A
{
#JsonProperty("secondary_key")
public final String secondaryKey;
#JsonCreator
B(#JsonProperty("primary_key") String primaryKey, #JsonProperty("secondary_key") String secondaryKey)
{
super(primaryKey);
this.secondaryKey = secondaryKey;
}
}
public class X
{
#JsonProperty("keys")
public final A keys;
#JsonCreator
X(#JsonProperty("keys") A keys)
{
this.keys = keys;
}
}
How can I use Jackson Polymorphic feature in order to correctly deserialize the below given json into their respective classes:
JSON A :
{ "keys" :{
"primary_key" : "abc"
}
}
JSON B :
{ "keys" : {
"primary_key" : "abc",
"secondary_key" : "xyz"
}
}
Expected Result: Map keys object to Class A for JSON A and Class B for JSON B.
Please suggest alternative suggestions too.
It feels like a pretty common problem and there is no easy annotations way to solve it (Or maybe i just cant find one):
Jackson Polymorphic Deserialization - Can you require the existence of a field instead of a specific value?
Deserializing polymorphic types with Jackson
One thing you can do is to add custom deserializer to your object mapper. Here is nice demo of this approach: https://stackoverflow.com/a/19464580/1032167
Here is demo related to your example:
import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.module.SimpleModule;
import java.io.IOException;
public class Main4 {
private static final String jsonA = "{ \"keys\" : { \"primary_key\" : \"abc\" } }";
private static final String jsonB =
"{ \"keys\" : { \"primary_key\" : \"abc\", \"secondary_key\" : \"xyz\" } }";
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
SimpleModule idAsRefModule = new SimpleModule("ID-to-ref");
idAsRefModule.addDeserializer(A.class, new AJsonDeserializer());
mapper.registerModule(idAsRefModule);
X tl = mapper.readValue(jsonA, X.class);
System.out.println(tl);
X t2 = mapper.readValue(jsonB, X.class);
System.out.println(t2);
}
public static class AJsonDeserializer extends JsonDeserializer<A>{
#Override
public A deserialize(JsonParser jp, DeserializationContext dc)
throws IOException {
ObjectCodec codec = jp.getCodec();
JsonNode node = codec.readTree(jp);
if (node.has("secondary_key")) {
return codec.treeToValue(node, B.class);
}
return new A(node.findValue("primary_key").asText());
}
}
public static class A
{
#JsonProperty("primary_key")
public final String primaryKey;
#JsonCreator
A(#JsonProperty("primary_key") String primaryKey)
{
this.primaryKey = primaryKey;
}
#Override
public String toString() {
return "A{" +
"primaryKey='" + primaryKey + '\'' +
'}';
}
}
public static class B extends A
{
#JsonProperty("secondary_key")
public final String secondaryKey;
#JsonCreator
B(#JsonProperty("primary_key") String primaryKey,
#JsonProperty("secondary_key") String secondaryKey)
{
super(primaryKey);
this.secondaryKey = secondaryKey;
}
#Override
public String toString() {
return "B{" +
"primaryKey='" + primaryKey + '\'' +
"secondaryKey='" + secondaryKey + '\'' +
'}';
}
}
public static class X
{
#JsonProperty("keys")
public final A keys;
#JsonCreator
X(#JsonProperty("keys") A keys)
{
this.keys = keys;
}
#Override
public String toString() {
return "X{" +
"keys=" + keys +
'}';
}
}
}
But you will have to create one more super class if you want to use default A deserializer or look here how you can solve this: https://stackoverflow.com/a/18405958/1032167
If I understoon correctly, simply passing the values will work, without any config. I believe this is what you are looking for:
public class Test {
private static final String JSON = "{\"keys\":{\"primary_key\":\"abc\",\"secondary_key\":\"xyz\"}}";
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
X x = mapper.readValue(JSON, X.class);
System.out.println(mapper.writeValueAsString(x));
}
}
class A {
private String primary_key;
public String getPrimary_key() {
return primary_key;
}
public void setPrimary_key(String primary_key) {
this.primary_key = primary_key;
}
}
class B extends A {
private String secondary_key;
public String getSecondary_key() {
return secondary_key;
}
public void setSecondary_key(String secondary_key) {
this.secondary_key = secondary_key;
}
}
class X {
private B keys;
public B getKeys() {
return keys;
}
public void setKeys(B keys) {
this.keys = keys;
}
}
Output will be:
{"keys":{"primary_key":"abc","secondary_key":"xyz"}}
In case this is not what you expect, please provide another explanation and I will edit the answer as needed.

Categories