How to deserialize raw JSON to Java object with Jackson - java

I've got two POJOs:
#lombok.Value
public class Foo {
String foo;
Bar bar;
}
#lombok.Value
public class Bar {
String bar;
String baz;
}
I'd like to be able to deserialize the following to a Foo instance:
{
"foo": "some foo",
"bar": "{ \"bar\": \"some bar\", \"baz\": \"some baz\" }"
}
If I understand it correctly this the exact opposite #JsonRawValue. There, it convert a Java String value (which is valid JSON value) to JSON object. But here, I need to convert a JSON string value to Java object.
I suspect that I need to write a custom deserializer, but I'm not sure how exactly since it involves parsing the raw JSON and assign it to the field. Maybe BeanDeserializerModifier? (I have no idea how to use it.)
I'd like to keep the object immutable (#Value), but I can drop this requirement if it helps solving the problem.
Thanks.

if you use FooDto class delete this Bar bar; from DtoClass
or use this
#lombok.Value
public class Foo {
String foo;
#JsonIgnore
Bar bar;
}

With the help of this question mentioned by #MichaƂ Ziober I managed to do it. Posting it here for reference since it was kinda tricky to get it working (with #Value and CamelCase property naming in JSON):
{
"Foo": "some foo",
"Bar": "{ \"bar\": \"some bar\", \"baz\": \"some baz\" }"
}
And the implementation is:
#Value
#Builder
#JsonDeserialize(builder = Foo.FooBuilder.class)
public class Foo {
#JsonAlias("Foo") // note this!
String foo;
Bar bar;
#JsonPOJOBuilder(withPrefix = "")
public static class FooBuilder {
#JsonAlias("Bar") // note this!
#JsonDeserialize(using = BarDeserializer.class)
public FooBuilder bar(Bar bar) {
this.bar = bar;
return this;
}
}
}
#lombok.Value
public class Bar {
String bar;
String baz;
}
public class BarDeserializer extends StdDeserializer<Bar> {
public BarDeserializer() {
super(Bar.class);
}
#Override
public Bar deserialize(JsonParser p, DeserializationContext ctx) throws IOException {
return (Bar) ((ObjectMapper) p.getCodec()).readValue(p.getValueAsString(), _valueClass);
}
}

Related

How to serialize/deserialize (json) a class that has an attribute that's an interface

I'd like to serialize/deserialize (json) a class that contains an attribute that is an interface, but the underlying class doesn't have any attributes. The below is my most simplified case and my best attempt at what to do.
This throws an error when trying to deserialize No suitable constructor found for type [simple type, class com.example.Bar]: can not instantiate from JSON object (need to add/enable type information?) at [Source: java.io.StringReader#301ec38b; line: 1, column: 2]
public interface FooInterface {
String doThing();
}
#JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
#EqualsAndHashCode
public class Foo implements FooInterface {
#Override
public String doThing() {
return "foo";
}
}
#Getter
#Setter
#EqualsAndHashCode
public class Bar {
FooInterface foo;
public Bar(FooInterface foo) {
this.foo = foo;
}
}
#Test
public void fooTest() throws IOException {
Foo foo = new Foo();
Bar bar = new Bar(foo);
String serialized = new ObjectMapper().writeValueAsString(bar); // = {"foo":{}}
Bar deserialized = new ObjectMapper().readValue(serialized, Bar.class);
Assert.assertEquals(bar, deserialized);
}
Please add default constructor to class Bar and I guess your issue should be resolved.
#Getter
#Setter
#EqualsAndHashCode
public class Bar {
FooInterface foo;
public Bar() {}
public Bar(FooInterface foo) {
this.foo = foo;
}
}
Do let me know if this doesn't solve your problem, I will try to dig deeper.
As #Aditya mentioned I was missing the default constructor which was causing the error I was having, but then the new error led me to finding this question which was the crux of the problem that this question was asking about.
Looks like I misunderstood what the JsonAutoDetect annotation did. Below is the code that ended up working for me.
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type")
#JsonSubTypes({
#JsonSubTypes.Type(value = Foo.class),
})
public interface FooInterface {
String doThing();
}
#EqualsAndHashCode
public class Foo implements FooInterface {
#Override
public String doThing() {
return "foo";
}
}
#Getter
#Setter
#EqualsAndHashCode
public class Bar {
FooInterface foo;
public Bar() {}
public Bar(FooInterface foo) {
this.foo = foo;
}
}
#Test
public void fooTest() throws IOException {
Foo foo = new Foo();
Bar bar = new Bar(foo);
String serialized = new ObjectMapper().writeValueAsString(bar); // {"foo":{"type":"Foo"}}
Bar deserialized = new ObjectMapper().readValue(serialized, Bar.class);
Assert.assertEquals(bar, deserialized);
}

"Inlining" an object with Jackson Annotations

With these classes
import com.fasterxml.jackson.annotation.JsonProperty;
public class Foo {
#JsonProperty
public String bar;
}
public class Bar {
#JsonProperty
public Foo foo;
#JsonProperty {
public String baz;
}
I can serialize and deserialize a Bar instance to/from JSON objects like this one:
{
"foo": { "bar": "bar" },
"baz": "baz"
}
Is there a Jackson annotation that will let me "inline" the foo field, so that my JSON representation becomes this?
{
"bar": "bar",
"baz": "baz"
}
I'm totally OK with it throwing errors in case of naming conflicts etc, but it would be nice if I didn't have to implement a custom serializer for this.
You can use #JsonUnwrapped:
Annotation used to indicate that a property should be serialized "unwrapped"; that is, if it would be serialized as JSON Object, its properties are instead included as properties of its containing Object.
Your Bar class would look like this:
public class Bar {
#JsonUnwrapped
private Foo foo;
#JsonProperty
private String baz;
}
And that produces your desired output. Removing #JsonProperty from the field didn't seem to make a difference, so I just omitted it.

Repeat json field with different name

i have a POJO mapped which i serialize using Jackson
public class Foo{
private String bar;
// public setter and getter for bar
}
it serializes to
{bar:"value"}
is there a jackson annotation to get another field in the JSON with the same value but with a different alias name, something like
{bar:"value", another_bar:"value"}
This should work for duplicating the value, though why you'd want to waste space like that is puzzling:
public class Foo {
private String bar;
#JsonProperty
public String getBar() {
return this.bar;
}
public void setBar(String bar) {
this.bar = bar;
}
#JsonProperty("another_bar")
public String getAnotherBar() {
return this.bar;
}
}

How to tell Jackson to ignore a field given that there is no condition on the field being null?

[Not a repeat].
I need the exact opposite of JsonIgnore. In my use case there is just one field that I need to add in serialization and all others are to be ignored. I've explored #JsonSerialize, #JsonInclude and #JsonProperty but none work as required by me.
Is there a way to achieve this or I'll need to mark the fields to be ignored with #JsonIgnore?
By default, Jackson will auto-detect all getters in your object and serialize their values. Jackson distinguished between "normal" getters (starting with "get") and "is-getters" (starting with "is", for boolean values). You can disable auto-detection for both entirely by configuring the ObjectMapper like this:
ObjectMapper mapper = new ObjectMapper();
mapper.disable(MapperFeature.AUTO_DETECT_GETTERS);
mapper.disable(MapperFeature.AUTO_DETECT_IS_GETTERS);
Alternatively, you can disable the auto-detection on a per class basis using #JsonAutoDetect. Annotate the fields or getters you actually do want to serialize with #JsonProperty.
#JsonAutoDetect(getterVisibility = JsonAutoDetect.Visibility.NONE,
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
public class MyObject {
private String foo;
#JsonProperty("bar")
private String bar;
private boolean fubar;
public MyObject(String foo, String bar, boolean fubar) {
this.foo = foo;
this.bar = bar;
this.fubar = fubar;
}
public String getFoo() {
return foo;
}
public String getBar() {
return bar;
}
public boolean isFubar() {
return fubar;
}
}
Serializing MyObject like this:
mapper.writeValueAsString(new MyObject("foo", "bar", true));
will result in the following JSON
{"bar":"bar"}
you can try this #JsonIgnoreProperties("propertyName") on the top of variable..

JACKSON JSON deserialization issue with overrloaded setter

From several reasons we have to make for developers convenience be able to set the one reference via overloaded setters ( this due it is modelled as oneOf attribute).
I would expect that depending on the JSON schema the polymorphic (oneOf) property would have the deserialized reference to object of FooType or BarType, ....
depending on the JSON schema.
I was hoping since FooType , BarType follow bean convention they can be easily determined like it happens for JacksonFeature in JAXRS ....
In my dummy test it seems to not work as described below :
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.configure(DeserializationFeature.READ_ENUMS_USING_TO_STRING, true);
mapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true);
mapper.readValue(SCHEMA, SimplePojo.class);
The issue is that the mapper crashes, and JSON schema(SCH1) can not be deserialized to POJO
The JSON schema (SCH1)
{
"dummy" : {
"bar" : "bar",
"baz" : 10
},
"other" : {
"foo" : "hi there"
},
"simple" : "simple"
}
The sub element types dummy, other look like :
public class BarType {
private String bar;
private Number baz;
public String getBar() {
return bar;
}
public void setBar(String bar) {
this.bar = bar;
}
public Number getBaz() {
return baz;
}
public void setBaz(Number baz) {
this.baz = baz;
}
and
public class FooType {
private Object foo;
public Object getFoo() {
return foo;
}
public void setFoo(Object foo) {
this.foo = foo;
}
The top level POJO ( i skipped some part )
public class SimplePojo {
private String simpleField;
private Object dummyField;
private Object otherField;
public String getSimple() {
return simpleField;
}
public void setSimple(String simple) {
this.simpleField = simple;
}
...
public void setDummy(final FooType dummy) {
this.dummyField = dummy;
}
public void setDummy(final BarType dummy) {
this.dummyField = dummy;
}
public void setDummy(final String dummy) {
this.dummyField = dummy;
}
the issue is that i can not deserialize correctly the schema (SCH1), instead I receive the :
com.fasterxml.jackson.databind.JsonMappingException: Conflicting setter definitions for property "dummy": com.hybris.api.poc.SimplePojo#setDummy(1 params) vs com.hybris.api.poc.SimplePojo#setDummy(1 params)
I was trying to use the #JsonCreator, and #JsonDeserialize but no luck it seems that i can not have two (non primitive) override setters
#JsonDeserialize( builder = FooType.FooTypeBuilder.class)
#JsonCreator
public void setDummy(final FooType dummy) {
this.dummyField = dummy;
}
/**
* Type specific setter for #dummy;
*
* #param dummy a reference to be set for #dummy
*/
#JsonDeserialize( builder = BarType.BarTypeBuilder.class )
#JsonCreator
public void setDummy(final BarType dummy) {
this.dummyField = dummy;
}
Can you hint me where I should the solution or am i breaking some principal concept ?
I do think you are breaking some principal concept there. For this type of scenario, having a base abstract class with JsonTypeInfo and JsonSubTypes annotations to describe your sub-object would probably be preferred. If you absolutely need the ability to set the three types via setDummy(...), would this work for you?
Replace:
public void setDummy(final FooType dummy) {
this.dummyField = dummy;
}
public void setDummy(final BarType dummy) {
this.dummyField = dummy;
}
public void setDummy(final String dummy) {
this.dummyField = dummy;
}
With:
#JsonDeserialize( using = DummyDeserializer.class )
public void setDummy(final Object dummy) {
// If you really need to restrict to the three types, throw exception here
if (! (dummy instanceof FooType || dummy instanceof BarType || dummy instanceof String) ) {
throw new Exception("Cannot setDummy dummy!");
}
this.dummyField = dummy;
}
This would require you to do the deserialization manually for all three classes in your DummyBuilder, but should solve your multi-setters problem. I've not tried to implement this, but think it works.
No, without inheritance structure Jackson has no way of automatically determining intended type during deserialization. If they did share the same base type, you could use #JsonTypeInfo to indicate how type id is included (usually as a property); and have a single setter (or creator property).
Otherwise you can not have conflicting setters (i.e. more than one with types that are not related to each other by sub-typing).

Categories