I currently have my POJO class as such for deserializing a json source.
public class OpenBuilding extends Building {
#JsonProperty("BuildingPostCode")
#Override
public String getPostcode() {
return super.getPostcode();
}
}
Where the parent class is as such
public abstract class Buidling {
protected String postcode;
public String getPostcode() {
return this.postcode;
}
}
My issue is that the String postcode isn't getting mapped at all. It works when using the annotation on the field. However since it is an inherited field and I have other children of Building, which use different property names for the same data, I cannot have it implemented in that way.
For example:
public class DirectedBuilding extends Building {
#JsonProperty("Pseudo_PostCode")
#Override
public String getPostcode() {
return super.getPostcode();
}
}
Perhaps try defining a constructor with #JsonCreator.
class Parent {
private final String foo;
public Parent(final String foo) {
this.foo = foo;
}
public String getFoo() {
return foo;
}
}
class Child extends Parent {
#JsonCreator
public Child(#JsonProperty("foo") final String foo) {
super(foo);
}
#JsonProperty("foo")
public String getFoo() {
return super.getFoo();
}
}
public static void main(String[] args) throws Exception {
final ObjectMapper objectMapper = new ObjectMapper();
final Child toSerialize = new Child("fooValue");
// Serialize the object to JSON
final String json = objectMapper.writer()
.withDefaultPrettyPrinter()
.writeValueAsString(toSerialize);
// Prints { "foo" : "fooValue" }
System.out.println(json);
// Deserialize the JSON
final Child deserializedChild = objectMapper.readValue(json, Child.class);
// Prints fooValue
System.out.println(deserializedChild.getFoo());
}
Related
I have a base class (Foo) with 2 children (A and B). They look like this:
public abstract class Foo {
private String fooString;
public Foo(String fooString) {
this.fooString = fooString;
}
//getter
}
#JsonDeserialize(builder = A.ABuilder.class)
public class A extends Foo {
private int amount;
public A(String fooString, int amount) {
super(fooString);
this.amount = amount;
}
//getter
#JsonPOJOBuilder
public static class ABuilder {
private String fooString;
private int amount;
public ABuilder withFooString(final String fooString) {
this.fooString = fooString;
return this;
}
public ABuilder withAmount(final int amount) {
this.amount = amount;
return this;
}
public A build() {
return new A(fooString, amount);
}
}
}
#JsonDeserialize(builder = B.BBuilder.class)
public class B extends Foo {
private String type;
public B(String fooString, String type) {
super(fooString);
this.type = type;
}
//getter
#JsonPOJOBuilder
public static class BBuilder {
private String fooString;
private String type;
public BBuilder withFooString(final String fooString) {
this.fooString = fooString;
return this;
}
public BBuilder withType(final String type) {
this.type = type;
return this;
}
public B build() {
return new B(fooString, type);
}
}
}
In my controller I have this endpoint:
#PutMapping
private ResponseEntity<Foo> doSomething(#RequestBody Foo dto) {
//stuff
}
But whenever I try to send over my json payload:
{
"fooString":"test",
"amount":1
}
I get the error:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.test.Foo` (no Creators, like default construct, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
at [Source: (String)"{"fooString":"test","amount":1}; line: 1, column: 1]
at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67)
at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1451)
at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1027)
at com.fasterxml.jackson.databind.deser.AbstractDeserializer.deserialize(AbstractDeserializer.java:265)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4013)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3004)
at AbstractJackson.main(AbstractJackson.java:11)
How do I get jackson to deserialize the json into the proper child class? What am I doing wrong?
The base class won't get the constructors of the sub classes instead it is quite the opposite,you cannot set subclass specific properties in base class instead you need to use specific subclass for the call or use custom deserializer for base class with correct use intanceOf
The easiest way to get it working is to change the controller method.
#PutMapping
private ResponseEntity<Foo> doSomething(#RequestBody A dto) {
//stuff
}
So i have this JSON file
{
"results":[
"result":{},
"result":{}
]
}
I wish to deserialize it to a java object which contains an array of result objects.
public class Foo(){
#JsonProperty("results")
private Result[] results;
public void setResults(Result[] results){
this.results = results;
}
public Result[] getResults(){
return this.results;
}
}
public class JsonToObject(){
ObjectMapper mp = new ObjectMapper();
public void createObject(String jsonFile){
Foo bar = mp.readValue(jsonFile, Foo.Class)
}
}
My issue is I keep getting deserialization issues as I have not definied "result".
One way I can get around this is to have result as a class variable inside Result but that seems stupid to do and also may cause issues with re-serializing.
How can I convert the JSON so that my class contains an array of result?
Your question program is wrong. There are many problems with your code. Please use below sample:
public class Foo {
#JsonProperty("results")
private Result[] results;
public Foo() {
}
public Result[] getResults() {
return results;
}
public void setResults(Result[] results) {
this.results = results;
}
}
public class Result {
private String name;
public Result() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public static void main(String[] args) throws IOException {
ObjectMapper mp = new ObjectMapper();
String content = "{\"results\":[{\"name\":\"apple\"},{\"name\":\"lemon\"}]}";
Foo bar = mp.readValue(content, Foo.class);
}
In my REST web service I need to accept JSON that can have two different structures.
Currently I have:
#Path("/")
public class MyAppResource {
...
#Context private HttpServletRequest request;
...
#POST
#Produces(MediaType.APPLICATION_JSON)
public MyResponseItem check(MyRequestItem body) {
...
}
}
where
public class MyRequestItem {
...
#JsonCreator
public MyRequestItem(#JsonProperty("data") ArrayList<TextItem> data) {
...
}
...
}
and
class TextItem {
...
#JsonCreator
public TextItem(#JsonProperty("count") int count,
#JsonProperty("text") String text) {
...
}
...
}
So it accepts JSON of the form {"data":[{"count":123,"text":"abc"},...]}.
In addition to the above format I need to accept this format: {"data":["abc",...]}. That is, I think I need to change TextItem so that it can either be a String or a class as above.
How to achieve this?
If you don't mind it to be the same class for both cases(TextItem), the easiest option for you is to add 1 more TextItem constructor with single string argument.
Here is demo:
public class Main {
public static String json1 = "{\"data\":[{\"count\":123,\"text\":\"abc\"}]}";
public static String json2 = "{\"data\":[\"abc\"]}";
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.readValue(json1, MyRequestItem.class));
System.out.println(mapper.readValue(json2, MyRequestItem.class));
}
#Data // lombok.Data;
#ToString // lombok.ToString;
public static class MyRequestItem {
private List<TextItem> data;
#JsonCreator
public MyRequestItem(#JsonProperty("data") ArrayList<TextItem> data) {
this.data = data;
}
}
#Data
#ToString
public static class TextItem {
private int count;
private String text;
#JsonCreator
public TextItem(#JsonProperty("count") int count,
#JsonProperty("text") String text) {
this.count = count;
this.text = text;
}
// this is the only thing you need to add to make it work
public TextItem( String text) {
this.text = text;
}
}
}
Result:
MyRequestItem(data=[TextItem(count=123, text=abc)])
MyRequestItem(data=[TextItem(count=0, text=abc)])
This is the JSON string:
{
"d":{
"results":[
{
"__metadata":{
"uri":"http://blabla1",
"type":"type1"
},
"Synonym":"miami"
},
{
"__metadata":{
"uri":"http://blabla2",
"type":"type2"
},
"Synonym":"florida"
}
]
}
}
This is the code:
public class Test{
#JsonIgnoreProperties(ignoreUnknown = true)
public static class d {
public List<Results> results;
public d() {
results = new ArrayList<Results>();
}
public static class Results {
public Metadata __metadata;
public String Synonym;
public Results() {}
}
public static class Metadata {
public String uri;
public String type;
public Metadata() {}
}
}
}
With the following mapper:
ObjectMapper mapper = new ObjectMapper();
Test.d readValue = mapper.readValue(jsonString, Test.d.class);
for (Test.d.Results k : readValue.results) {
System.out.println("synonym: "+k.Synonym);
}
It gives me no error, just an empty arraylist of results...
p.s. i have made a lot of changes in between time, sorry for the inconvenience
you must create an object that fits with the jSon answer, something like this (not tested):
class d {
public List<Results> results;
public d() {
}
}
class Results {
public Metadata metadata;
public String synonym;
public Results() {
}
}
class Metadata {
public String uri;
public String type;
public Metadata() {
}
}
Hope it helps!
I have managed to solve it.
i forgot to make setters and getters for class 'd'.
public class Test {
private d d;
public d getD() {return d;}
public void setD(d d) {this.d = d;}
public static class d{
private List<Results> results;
public List<Results> getResults() {return results;}
public void setResults(List<Results> results) {this.results = results;}
}
public static class Results {
public Metadata __metadata;
public String Synonym;
}
public static class Metadata {
private String uri;
private String type;
public String getUri() {return uri;}
public void setUri(String uri) {this.uri = uri;}
public String getType() {return type;}
public void setType(String type) {this.type = type;}
}
}
This is the map:
Test test = mapper.readValue(json, KeyPhrase.class);
System.out.println("cp");
for(Test.Results res : test.getD().getResults()){
System.out.println(res.Synonym);
}
i have a question concerning Json deserialization with Jackson (edit: 2.0.4 version). I would like serialize an Bean, containing the list of other beans, as string , save this string and then to deserialize later this string. I use the some base class and its subtypes. The basis class Parent is an abstract class, that has two attributes with getters und setters, this class has also an abstract method getType(). Other abstract class AbstractChild inherits from class Parent . This class has attributes too and isExportEnabled() abstract method.
I have no problems, if this Bean will be serialized. I use the following annotation on the Parent class
*#JsonTypeInfo(use = JsonTypeInfo.Id.MINIMAL_CLASS, include = JsonTypeInfo.As.PROPERTY, property = "#cls")*
The string will be generated.
But the desirialize failed: the exception “Unrecognized field "type"”will be thrown. But I need this attribute! I’m tried to set #JsonProperty("type") on abstract method, this has no effect.
Please help me.
edit: if i introduce the private fields "type" (Parent) and "exportEnabled" (AbstractChild) so it runs correctly.
P.S The exception
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException:
Unrecognized field "type" (class tst.SimpleTestMain$FirstChild), not
marked as ignorable (4 known properties: , "id", "maxCount", "code",
"minCount"]) at [Source: java.io.StringReader#1ad9fa; line: 1,
column: 125] (through reference chain:
tst.Fam["members"]->tst.FirstChild["type"]) at
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:79)
at
com.fasterxml.jackson.databind.DeserializationContext.reportUnknownProperty(DeserializationContext.java:568)
…and the example class
package tst;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.ObjectMapper;
public class SimpleTestMain {
enum Type {
TYPE_A, TYPE_B
}
#JsonTypeInfo(use = JsonTypeInfo.Id.MINIMAL_CLASS, include = JsonTypeInfo.As.PROPERTY, property = "#cls")
public static abstract class Parent {
private int id;
private String code;
public Parent() {
}
#JsonProperty("type")
// First abstract getter
public abstract Type getType();
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
}
public static abstract class AbstractChild extends Parent {
private int minCount;
private int maxCount;
public AbstractChild() {
}
// Second abstract method: boolean used
public abstract boolean isExportEnabled();
public int getMinCount() {
return minCount;
}
public void setMinCount(int minCount) {
this.minCount = minCount;
}
public int getMaxCount() {
return maxCount;
}
public void setMaxCount(int maxCount) {
this.maxCount = maxCount;
}
}
public static class FirstChild extends AbstractChild {
#Override
public boolean isExportEnabled() {
return false;
}
#Override
public Type getType() {
return Type.TYPE_A;
}
}
public static class SecondChild extends AbstractChild {
#Override
public boolean isExportEnabled() {
return true;
}
#Override
public Type getType() {
return Type.TYPE_B;
}
}
public static class Fam {
private int famId;
private List<Parent> members;
public Fam() {
members = new ArrayList<Parent>();
}
public int getFamId() {
return famId;
}
public void setFamId(int famId) {
this.famId = famId;
}
public List<Parent> getMembers() {
return members;
}
public void setMembers(List<Parent> members) {
this.members = members;
}
public void addMember(Parent member) {
members.add(member);
}
}
public SimpleTestMain() {
}
public static void main(String[] args) {
Fam fam = new Fam();
FirstChild fc = new FirstChild();
fc.setId(1);
fc.setCode("FirstChildCode");
fc.setMinCount(1);
fc.setMaxCount(4);
fam.addMember(fc);
SecondChild sc = new SecondChild();
sc.setCode("SecondChildCode");
sc.setMinCount(131);
sc.setMaxCount(431);
fam.addMember(sc);
String test = "";
// Serialize it
ObjectMapper mapper = new ObjectMapper();
try {
test = mapper.writeValueAsString(fam);
System.out.println("Serialized bean:\n" + test);
// the output
// Serialized bean:
// {"famId":0,"members":[{"#cls":".SimpleTestMain$FirstChild","id":1,"code":"FirstChildCode","minCount":1,"maxCount":4,"type":"TYPE_A","exportEnabled":false},{"#cls":".SimpleTestMain$SecondChild","id":0,"code":"SecondChildCode","minCount":131,"maxCount":431,"type":"TYPE_B","exportEnabled":true}]}
} catch (IOException e) {
e.printStackTrace();
}
// Deserialize it
mapper = new ObjectMapper();
// mapper.enableDefaultTyping();
try {
Fam fam1 = mapper.readValue(test, Fam.class);
} catch (IOException e) {
e.printStackTrace();
}
}
}