Unable to deserialise via mixin - java
Attached is my json. I am receiving this from an endpoint. I have the object type in my consuming app. the object contains; success, message and loggedInMember (which is an object). In the json, the feeClasses contains a lot of fields and objects etc which is why i have not included the whole json. I am only interested in the success and message fields.
{
"header":{
"messageId":null,
"receivedTimestamp":1611246394839,
"replyTo":null,
"redelivered":false,
"originator":null
},
"internalId":null,
"auditPoints":[
],
"success":true,
"message":"",
"loggedInMember":{
"feeClasses":{
...CONTAINS A LOT OF FIELDS...
}
}
}
I am trying to map this response to the object type, hence essentially leaving loggedInMembers null. This is my test I am trying to run:
public void test() throws JsonProcessingException
{
//String json = "{\"header\":{\"messageId\":null,\"bucketNumber\":null,\"senderSubId\":null,\"senderLocationId\":null,\"onBehalfOfCompId\":null,\"onBehalfOfSubId\":null,\"onBehalfOfLocationId\":null,\"correlationId\":null,\"receivedTimestamp\":1611246394839,\"replyTo\":null,\"redelivered\":false,\"originator\":null},\"internalId\":null,\"auditPoints\":[],\"success\":true,\"message\":\"\",\"loggedInMember\":{\"memberLoginName\":\"BMARTINTEST\",\"memberId\":\"201901241246290000036402D\",\"settlementAccountIds\":[\"201901241246290000036491D\"],\"parentMemberId\":\"1\",\"firmId\":\"990\",\"memberType\":\"INDIVIDUAL\",\"memberAccountType\":\"PROD\",\"password\":\"D1208B304FD7AA6187690A389A5040C1D9B07643\",\"feeClasses\":{\"byId\":{\"201902120947520000559606D\":{\"memberLoginName\":\"BMARTINTEST\",\"feeClassId\":\"201508041827550000942152D\",\"memberFeeClassId\":\"201902120947520000559606D\",\"allocatedDate\":{\"year\":2019,\"month\":2,\"day\":12,\"timeMillis\":1549929600000},\"firstUsedForTradeDate\":{\"year\":2019,\"month\":2,\"day\":12,\"timeMillis\":1549929600000},\"firstUsedForSettlementDate\":null,\"usableFromDate\":{\"year\":2019,\"month\":2,\"day\":12,\"timeMillis\":1549929600000},\"usableToDate\":{\"year\":2019,\"month\":2,\"day\":19,\"timeMillis\":1550534400000},\"usableToTimestamp\":1550613600000,\"usableBusinessDaysAllocated\":6,\"usableBusinessDaysRemaining\":0,\"narrative\":\"Bonus assigned to member at first-time funding of amount 4000.00 : Set expiration date/time\",\"disabled\":false,\"usableForTrade\":true,\"usableForSettlement\":true},\"202001290940390000868824D\":{\"memberLoginName\":\"BMARTINTEST\",\"feeClassId\":\"202001290940340000776406D\",\"memberFeeClassId\":\"202001290940390000868824D\",\"allocatedDate\":{\"year\":2020,\"month\":1,\"day\":29,\"timeMillis\":1580256000000},\"firstUsedForTradeDate\":null,\"firstUsedForSettlementDate\":null,\"usableFromDate\":{\"year\":2020,\"month\":1,\"day\":6,\"timeMillis\":1578268800000},\"usableToDate\":{\"year\":2020,\"month\":2,\"day\":27,\"timeMillis\":1582761600000},\"usableToTimestamp\":1582840800000,\"usableBusinessDaysAllocated\":0,\"usableBusinessDaysRemaining\":0,\"narrative\":\"Added NO_FEES_CLASS\",\"disabled\":false,\"usableForTrade\":true,\"usableForSettlement\":true},\"201901241246290000036417D\":{\"memberLoginName\":\"BMARTINTEST\",\"feeClassId\":\"201508041736360000943781D\",\"memberFeeClassId\":\"201901241246290000036417D\",\"allocatedDate\":{\"year\":2019,\"month\":1,\"day\":24,\"timeMillis\":1548288000000},\"firstUsedForTradeDate\":null,\"firstUsedForSettlementDate\":null,\"usableFromDate\":{\"year\":2019,\"month\":1,\"day\":24,\"timeMillis\":1548288000000},\"usableToDate\":null,\"usableToTimestamp\":null,\"usableBusinessDaysAllocated\":0,\"usableBusinessDaysRemaining\":0,\"narrative\":null,\"disabled\":false,\"usableForTrade\":true,\"usableForSettlement\":true}},\"empty\":false},\"legalName\":\"Martin Birch\",\"taxId\":\"345335454\",\"taxCountryId\":\"US\",\"currency\":\"USD\",\"lastTradeId\":null,\"introducingBrokerMemberId\":null,\"introducingBrokerMemberName\":null,\"introducingBrokerMemberCode\":null,\"clearedByMemberId\":\"SECOND_TEST\",\"clearedByMemberLoginName\":null,\"memberProblems\":[],\"emailNotificationEnabled\":true,\"rtafLevelId\":0,\"rtafAmount\":0,\"maxNumberOfPositionAccounts\":1,\"ciciIdentifier\":null,\"traderRequired\":false,\"interestClass\":\"INDIVIDUAL\",\"memberCreatedDate\":1548333989000,\"parentMemberLoginNames\":[\"NADEX.COM\",\"NADEX\"],\"demoStartDate\":null,\"demoEndDate\":null,\"clientIdMaxLimit\":null,\"memberAccountApplicationFieldData\":null,\"rank\":0,\"uuid\":\"201901241246290000036395D\",\"referrerId\":\"raf4qam5h00s36d\",\"testMember\":false},\"allReplyToSource\":[],\"sendToOriginatorOnly\":false}";
String json = "{\n" +
" \"header\":{\n" +
" \"messageId\":null,\n" +
" \"receivedTimestamp\":1611246394839,\n" +
" \"replyTo\":null,\n" +
" \"redelivered\":false,\n" +
" \"originator\":null\n" +
" },\n" +
" \"internalId\":null,\n" +
" \"auditPoints\":[\n" +
" \n" +
" ],\n" +
" \"success\":true,\n" +
" \"message\":\"\",\n" +
" \"loggedInMember\":{\n" +
" \"feeClasses\":{\n" +
" \n" +
" }\n" +
" }\n" +
"}";
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
objectMapper.addMixIn(LogonResponseMessage.class, LogonResponseMixin.class);
LogonResponseMessage responseMessage = objectMapper.readValue(json, LogonResponseMessage.class);
System.out.println(responseMessage);
}
My mixin:
public abstract class LogonResponseMixin
{
LogonResponseMixin(#JsonProperty("success") boolean success, #JsonProperty("message") String message){};
#JsonIgnore
abstract Member loggedInMember();
#JsonIgnore
abstract MemberFeeClasses feeClasses();
#JsonIgnore
abstract Header header();
}
I am getting the following error: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of domain.xyz.MemberFeeClasses (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
Am i creating the mixin wrong? I have asked in a previous question and using mixin was the general consensus but it doesn't seem to play ball with me.
Thank you.
The reason why MemberFeeClasses cannot be constructed is the same as your initial problem, just add a mixin for all classes
See the example below:
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Getter;
import lombok.Setter;
import java.io.IOException;
public class Test {
//------------------------------------------------//
// PART 1 - MAIN TEST METHOD
//------------------------------------------------//
public static void main(String[] args) throws IOException {
String json = "{\n" +
" \"header\":{" +
" },\n" +
" \"success\":true,\n" +
" \"message\":\"\",\n" +
" \"loggedInMember\":{\n" +
" \"feeClasses\":{\n" +
" \"amount\": \"20\"\n" +
" }\n" +
" }\n" +
"}";
ObjectMapper objectMapper = new ObjectMapper();
//objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
objectMapper.addMixIn(LogonResponseMessage.class, LogonResponseMixin.class);
objectMapper.addMixIn(Member.class, MemberMixin.class);
objectMapper.addMixIn(MemberFee.class, MemberFeeMixin.class);
LogonResponseMessage responseMessage = objectMapper.readValue(json, LogonResponseMessage.class);
System.out.println(responseMessage.loggedInMember.feeClasses.amount);
}
//------------------------------------------------//
// PART 2 - CREATOR MIXIN
//------------------------------------------------//
static abstract class LogonResponseMixin {
LogonResponseMixin(#JsonProperty("success") boolean success,
#JsonProperty("message") String message,
#JsonProperty("header") Header header,
#JsonProperty("loggedInMember") Member member) {
}
}
static abstract class MemberMixin {
MemberMixin(#JsonProperty("feeClasses") MemberFee feeClasses) {
}
}
static abstract class MemberFeeMixin {
#JsonCreator
MemberFeeMixin(#JsonProperty("amount") String amount) {
}
}
//------------------------------------------------//
// PART 3 - EXAMPLE CLASS DEFINITION
//------------------------------------------------//
static class Header {
}
#Getter
#Setter
static class Member {
private MemberFee feeClasses;
public Member(MemberFee feeClasses) {
this.feeClasses = feeClasses;
}
}
#Getter
#Setter
static class MemberFee {
private String amount;
public MemberFee(String amount) {
this.amount = amount;
}
}
#Getter
#Setter
static class LogonResponseMessage {
private boolean success;
private String message;
private Header header;
private Member loggedInMember;
public LogonResponseMessage(boolean success, String message, Header header, Member member) {
this.success = success;
this.message = message;
this.header = header;
this.loggedInMember = member;
}
}
}
Related
Does Jackson not pretty print values annotated by #JsonRawValue?
I store JSON in my database and want to include this JSON in an API response as-is, without de-serializing before serializing the data. The data itself resides in a wrapper object. When serializing this wrapper, it appears the JSON from my database isn't pretty-printed alongside the rest of the data, giving really weird-looking responses. I have written some example code to outline my issue: import com.fasterxml.jackson.annotation.JsonRawValue; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; public class JacksonTest { private static final String EXPECTED_OUTPUT = "{\n" + " \"wrapper\" : {\n" + " \"data\" : {\n" + " \"raw\" : \"test\"\n" + " }\n" + " }\n" + "}"; private static final String RAW_JSON = "{\n" + " \"raw\" : \"test\"\n" + "}"; static class Pojo { #JsonRawValue private final String data; public Pojo(String data) { this.data = data; } public String getData() { return data; } } static class Wrapper { private final Pojo wrapper; public Wrapper() { wrapper = new Pojo(RAW_JSON); } #SuppressWarnings("unused") public Pojo getWrapper() { return wrapper; } } #Test void shouldEqual() throws JsonProcessingException { ObjectMapper mapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT); String output = mapper.writeValueAsString(new Wrapper()); assertThat(output).isEqualTo(EXPECTED_OUTPUT); } } This test fails with the following output: { "wrapper" : { "data" : { "raw" : "test" } } } While I expect jackson to give me the following output: { "wrapper" : { "data" : { "raw" : "test" } } } Is there any way to "fix" the indenting of the raw data that's annotated with #JsonRawValue?
Maybe with the following code your test will pass : Object json = mapper.readValue(input, Object.class); String indented = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(json); You can check that stackoverflow question and its own answers from which the code I have written upper was coming from, from the accepted answer : Convert JSON String to Pretty Print JSON output using Jackson
Apply a mask on a JSON to keep only mandatory data
I have an API which takes some potentially mandatory data to create a temporary session: e.g.: at first, my POST /signup endpoint user need to send me the following data: { "customer": { "age": 21, "ssn": "000 00 0000", "address": { "street": "Customer St.", "phone": "+66 444 333 222" } } } Let's call it JSON a. On the other hand, I have some legal partners which requires some of these data, but not all of them: e.g.: { "customer": { "ssn": "The SSN is mandatory to register against XXX Company", "address": { "phone": "XXX Company will send a text message to validate your registration" } } } Let's call it JSON b. Due to recent legal restrictions, in my information system, I have to keep only mandatory data for the user to carry with his chosen workflow. Hence my question: is there a function (either built in Jackson or some other JSON handling library or an algorithm you would recommend) I could apply such that given JSON b and JSON a, it would output the following JSON: { "customer": { "ssn": "000 00 0000", "address": { "phone": "+66 444 333 222" } } } Thinking a bit, I found something which might be a solution: merge JSON a with JSON b, on conflict take JSON b values, name the result JSON c make a diff (drop equals data) between JSON c and JSON a, on conflict take JSON a values I know merging two JSON using Jackson can be done using com.fasterxml.jackson.databind.ObjectMapper#readerForUpdating, so my question could be reduced to: is there a way to make a diff between two JSON and give a conflict resolution function?
I'd suggest to use token/event/stream-based solution. The following is just an illustration using tiny parser/generator lib https://github.com/anatolygudkov/green-jelly (both Gson and Jackson also provide stream-oriented API): import org.green.jelly.AppendableWriter; import org.green.jelly.JsonEventPump; import org.green.jelly.JsonNumber; import org.green.jelly.JsonParser; import java.io.StringWriter; import java.io.Writer; import java.util.Arrays; import java.util.HashSet; import java.util.Set; public class FilterMyJson { private static final String jsonToFilter = "{\n" + " \"customer\": {\n" + " \"age\": 21,\n" + " \"ssn\": \"000 00 0000\",\n" + " \"address\": {\n" + " \"street\": \"Customer St.\",\n" + " \"phone\": \"+66 444 333 222\"\n" + " }\n" + " }\n" + "}"; public static void main(String[] args) { final StringWriter result = new StringWriter(); final JsonParser parser = new JsonParser(); parser.setListener(new MyJsonFilter(result, "age", "street")); parser.parse(jsonToFilter); // if you read a file with a buffer, // call parse() several times part by part in a loop until EOF parser.eoj(); // and then call .eoj() System.out.println(result); } static class MyJsonFilter extends JsonEventPump { private final Set<String> objectMembersToFilter; private boolean currentObjectMemberIsAllowed; MyJsonFilter(final Writer output, final String... objectMembersToFilter) { super(new AppendableWriter<>(output)); this.objectMembersToFilter = new HashSet<>(Arrays.asList(objectMembersToFilter)); } #Override public boolean onObjectMember(final CharSequence name) { currentObjectMemberIsAllowed = !objectMembersToFilter.contains(name.toString()); return super.onObjectMember(name); } #Override public boolean onStringValue(final CharSequence data) { if (!currentObjectMemberIsAllowed) { return true; } return super.onStringValue(data); } #Override public boolean onNumberValue(final JsonNumber number) { if (!currentObjectMemberIsAllowed) { return true; } return super.onNumberValue(number); } } } prints: { "customer": { "ssn": "000 00 0000", "address": { "phone": "+66 444 333 222" } } } The code is quite simplified. For now it filters out only string and number scalars. No object hierarchy is supported. You may need to improve the code for some cases therefore. Props of such type of solution: the file/data doesn't require to be loaded entirely into memory, you can process megs/gigs with no problems it works much more faster, especially for large files it's easy to implement any custom type/rule of transformation with this pattern. For example, it is easy to have your filter to be parametrized, you don't have to recompile the code each time your data structure is changed
Thanks to https://github.com/algesten/jsondiff which gave me the keywords to find a more maintained https://github.com/java-json-tools/json-patch (ability to use your own ObjectMapper), I have found my answer (see mask() method): import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import com.fasterxml.jackson.annotation.JsonMerge; import com.fasterxml.jackson.databind.ObjectMapper; import com.github.fge.jsonpatch.diff.JsonDiff; import java.math.BigInteger; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.SneakyThrows; import lombok.ToString; import org.junit.jupiter.api.Test; class JsonPatchTest { private static final ObjectMapper mapper = new ObjectMapper(); #Getter #Builder #ToString #EqualsAndHashCode #NoArgsConstructor(access = AccessLevel.PRIVATE) #AllArgsConstructor(access = AccessLevel.PRIVATE) public static class Data { #JsonMerge Customer customer; #Getter #Builder #ToString #EqualsAndHashCode #NoArgsConstructor(access = AccessLevel.PRIVATE) #AllArgsConstructor(access = AccessLevel.PRIVATE) public static class Customer { #JsonMerge BigInteger age; #JsonMerge String ssn; #JsonMerge Address address; #Getter #Builder #ToString #EqualsAndHashCode #NoArgsConstructor(access = AccessLevel.PRIVATE) #AllArgsConstructor(access = AccessLevel.PRIVATE) public static class Address { #JsonMerge String street; #JsonMerge String phone; } } #SneakyThrows Data merge(Data parent) { var originCopyAsString = mapper.writerFor(this.getClass()).writeValueAsString(this); var parentAsString = mapper.writerFor(this.getClass()).writeValueAsString(parent); var parentCopy = mapper.readerFor(this.getClass()).readValue(parentAsString); var clone = mapper.readerForUpdating(parentCopy).readValue(originCopyAsString); return (Data) clone; } } #SneakyThrows #Test void mask() { final var diff = JsonDiff.asJsonPatch(mapper.readTree(jsonC()), mapper.readTree(jsonB())); final var masked = diff.apply(mapper.readTree(jsonA())).toPrettyString(); assertThat(masked).isEqualToIgnoringWhitespace(masked()); } private String jsonA() { return "{\n" + " \"customer\": {\n" + " \"age\": 21,\n" + " \"ssn\": \"000 00 0000\",\n" + " \"address\": {\n" + " \"street\": \"Customer St.\",\n" + " \"phone\": \"+66 444 333 222\"\n" + " }\n" + " }\n" + "}"; } private String jsonB() { return "{\n" + " \"customer\": {\n" + " \"ssn\": \"The SSN is mandatory to register against XXX Company\",\n" + " \"address\": {\n" + " \"phone\": \"XXX Company will send a text message to validate your registration\"\n" + " }\n" + " }\n" + "}"; } #SneakyThrows private String jsonC() { final Data dataA = mapper.readerFor(Data.class).readValue(jsonA()); final Data dataB = mapper.readerFor(Data.class).readValue(jsonB()); final Data merged = dataB.merge(dataA); return mapper.writerFor(Data.class).writeValueAsString(merged); } #SneakyThrows private String masked() { return "{\n" + " \"customer\": {\n" + " \"ssn\": \"000 00 0000\",\n" + " \"address\": {\n" + " \"phone\": \"+66 444 333 222\"\n" + " }\n" + " }\n" + "}"; } }
Jackson Array to Pojo Not Working Only on Android
I want to convert json array to POJO, it is working when running on JVM but failed on Android This is my pojo: package com.binance.api.client.domain.market; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonPropertyOrder; #JsonFormat(shape = JsonFormat.Shape.ARRAY) #JsonPropertyOrder() #JsonIgnoreProperties(ignoreUnknown = true) public class Lilin { public Long openTime; public String open; public String high; public String low; public String close; public String volume; public Long closeTime; public String quoteAssetVolume; public Long numberOfTrades; public String takerBuyBaseAssetVolume; public String takerBuyQuoteAssetVolume; } And then test it manually: public void testCandlestickDeserializer() { final String candlestickJson = "[\n" + " 1499040000000,\n" + " \"0.01634790\",\n" + " \"0.80000000\",\n" + " \"0.01575800\",\n" + " \"0.01577100\",\n" + " \"148976.11427815\",\n" + " 1499644799999,\n" + " \"2434.19055334\",\n" + " 308,\n" + " \"1756.87402397\",\n" + " \"28.46694368\",\n" + " \"17928899.62484339\"\n" + " ]"; ObjectMapper mapper = new ObjectMapper(); try { Lilin candlestick = mapper.readValue(candlestickJson, Lilin.class); System.out.println(candlestick); } catch (IOException e) { System.err.println(e); } } There is no error when try it on JVM but raises this error when run it on Android: Cannot deserialize value of type `java.lang.Long` from String "0.01634790": not a valid Long value it seems the #JsonPropertyOrder() annotation is not working properly on Android
It's possible that you might have missed to define the property ordering, e.g from the docs: Examples: // ensure that "id" and "name" are output before other properties #JsonPropertyOrder({ "id", "name" }) // order any properties that don't have explicit setting using alphabetic order #JsonPropertyOrder(alphabetic=true) //This annotation may or may not have effect on deserialization: for basic JSON handling there is no effect, but for other supported data types (or structural conventions) there may be. Source: https://fasterxml.github.io/jackson-annotations/javadoc/2.2.0/com/fasterxml/jackson/annotation/JsonPropertyOrder.html
Globally ignore class in Jackson
Jackson has ability to skip unknow properties globally using DeserializationFeature, but I can't find any global config to ignore whole class from being parsed. I have class with two methods with the same name but with different arguments, so I want to set this class as ignorable globally(using objectMapper object) not just by adding any annotations to model class. May be someone faced with such problem. Sorry for bad English.
One option is to mark the type you wish to ignore with #JsonIgnoreType annotation. If you don't want to mess your model with Jackson annotations you can use mix-ins. Another option is to override the Jackson annotation introspector to ignore the property based on its type. Here is example shows both: public class JacksonIgnoreByType { public static final String JSON = "{\n" + " \"bean1\" : {\n" + " \"field1\" : \"value1\"\n" + " },\n" + " \"bean2\" : {\n" + " \"field2\" : \"value2\"\n" + " },\n" + " \"bean3\" : {\n" + " \"field3\" : \"value3\"\n" + " }\n" + "}\n"; public static class Bean1 { public String field1; #Override public String toString() { return "Bean1{" + "field1='" + field1 + '\'' + '}'; } } #JsonIgnoreType public static class Bean2 { public String field2; } public static class Bean3 { public String field3; } public static class Bean4 { public Bean1 bean1; public Bean2 bean2; public Bean3 bean3; #Override public String toString() { return "Bean4{" + "bean1=" + bean1 + ", bean2=" + bean2 + ", bean3=" + bean3 + '}'; } } public static void main(String[] args) throws IOException { ObjectMapper mapper = new ObjectMapper(); mapper.setAnnotationIntrospector(new JacksonAnnotationIntrospector(){ #Override public boolean hasIgnoreMarker(AnnotatedMember m) { return m.getRawType() == Bean3.class || super.hasIgnoreMarker(m); } }); System.out.println(mapper.readValue(JSON, Bean4.class)); } } Output: Bean4{bean1=Bean1{field1='value1'}, bean2=null, bean3=null}
How can i deserialize Json string where object's field's subclass is included in json string, using Java and Jackson library
I am very new to Java. I have some classes Site, Instances, CloudInstance. Class Site has an attribute instances and class CloudInstance inherits class Instance. They are as follows- public class Site extends BaseEntity { private String siteName; List<Instance> instances = Lists.newArrayList(); } public class Instance extends BaseEntity { private String instanceId; private String name; } public class CloudInstance extends Instance { private String availabilityZone; private String instanceType } I am deserializing json string as follows - import com.fasterxml.jackson.databind.ObjectMapper; ObjectMapper mapper = new ObjectMapper(); BaseEntity obj = null; obj = (BaseEntity) mapper.readValue(jsonStr, Site.class); It works fine if my jsonStr does not contain fields of class 'CloudInstance' and contains field instance with Instance class's fields. Problem - Now i want to deserialize the jsonStr which includes 'CloudInstance' classe's fiels as well as the part of 'instances' field of class 'Site'. Ex jsonStr is as follows - { "id": null, "siteName": "demo", "instances": [ { "instanceId": "i-8c2ee5fc", "name": "some-node", "availabilityZone": "some-zone", "instanceType": "t1.micro" }] } For the above jsonStr i get following error error: Unrecognized field \"availabilityZone\" and error: Unrecognized field \"instanceType\" With lots of if else and dirty code i can get the obj of Site including above fields. But i want to implement clean solution for this. Is there any library which can do this? Any help id valuable. Please help..!! Thanks in advance.
What you are trying to achieve is called polymorphic deserialization. Your example fails because Jackson needs to know what instance type should be constructed from JSON and placed to the list of instances. Please refer to this wiki page for detailed explanation. I have modified you example to demonstrate how it could work. I've added the instance type information in the #type field in the JSON representation. Also I've made all the classes immutable using constructors annotated with the #JsonCreator annotation to create instances. public class JacksonPolymorphism { public static class BaseEntity { private final String id; protected BaseEntity(String id) { this.id = id; } } public static class Site extends BaseEntity { private final String siteName; private final List<Instance> instances; #JsonCreator public Site(#JsonProperty("id") String id, #JsonProperty("siteName") String siteName, #JsonProperty("instances") List<Instance> instances) { super(id); this.siteName = siteName; this.instances = instances; } #Override public String toString() { return "Site{" + "siteName='" + siteName + '\'' + ", instances=" + instances + '}'; } } #JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "#type") #JsonTypeName(value = "simple") public static class Instance extends BaseEntity { private final String name; #JsonCreator public Instance(#JsonProperty("instanceId") String id, #JsonProperty("name") String name) { super(id); this.name = name; } #Override public String toString() { return "Instance{" + "name='" + name + '\'' + '}'; } } #JsonTypeName("cloud") public static class CloudInstance extends Instance { private final String availabilityZone; private final String instanceType; public CloudInstance(#JsonProperty("instanceId") String id, #JsonProperty("name") String name, #JsonProperty("availabilityZone") String availabilityZone, #JsonProperty("instanceType") String instanceType) { super(id, name); this.availabilityZone = availabilityZone; this.instanceType = instanceType; } #Override public String toString() { return "CloudInstance{" + "availabilityZone='" + availabilityZone + '\'' + ", instanceType='" + instanceType + '\'' + '}'; } } public static final String JSON = "{\n" + " \"id\": null,\n" + " \"siteName\": \"demo\",\n" + " \"instances\": [\n" + " {\n" + " \"#type\": \"cloud\",\n" + " \"instanceId\": \"i-8c2ee5fc\",\n" + " \"name\": \"some-node\",\n" + " \"availabilityZone\": \"some-zone\",\n" + " \"instanceType\": \"t1.micro\" \n" + " }," + " {\n" + " \"#type\": \"simple\",\n" + " \"instanceId\": \"ABC\",\n" + " \"name\": \"FGF\"\n" + " }]" + " }"; public static void main(String[] args) throws IOException { ObjectMapper mapper = new ObjectMapper(); mapper.registerSubtypes(CloudInstance.class); System.out.println(mapper.readValue(JSON, Site.class)); } } Output: Site{siteName='demo', instances=[CloudInstance{availabilityZone='some-zone', instanceType='t1.micro'}, Instance{name='FGF'}]}
I always had problems to deserialize JSON that contains List<...> objects with Jackson, so try to deserialize with Gson: https://code.google.com/p/google-gson/ Take a look at the documentation in the methods fromJson and toJson. I hope that can help, Best regards
Never had consistent luck with Jackson or Gson, try Flex JSON instead: JSONSerializer ser = new JSONSerializer(); String json = ser.deepSerialize(yourObject); JSONDeserializer<YourMainType> der = new JSONDeserializer<YourMainType>(); YourMainType mainType = der.deserialize(json); For this to work, all classes subject to serialization/deserialization must expose getters/setters consistent with Java Beans convention.
You can used GSON Library for de-serialize your Json into your class object. There is function gson.fromJson(JSON String) which convert json string to class object. Here is Sample code : Gson json = new Gson(); Site site = json.fromJson(jsonStr, Site.class); But in your code you have replace List<Instance> instances = Lists.newArrayList(); this line in class Site with List<CloudInstance> instances = new ArrayList<CloudInstance>(); Because your CloudInstance class extend Instance it means CloudInstance class include member of Instance class. As per json you need to do this to cast directly into class object. May this will help you.