I'd like to go further on what this question was about, I've been roaming SO for a solid hour now without finding anything.
Basically, what I'm trying to do is having a property properly instanciated through Jackson internal reflection algorithm during deserialization but having this same property not serialized when it comes to serialization.
I know about #JsonIgnore and #JsonIgnoreProperties but apparently I can't seem to use them right : either my property is correctly deserialized when I feed Jackson a proper map of properties but it also appears in the serialized results, either (when using #JsonIgnore) it is not serialized (which is wanted) but also not deserialized (not wanted).
Example :
public class Foo {
/* This is the property I want to be instanciated by Jackson upon deserialization
* but not serialized upon serialization
*/
private final Object bar = null;
public Object getBar() {
return bar;
}
}
To make things worse, as you can see, the property is final (this is why I'm keen on using Jackson reflection ability upon Foo instanciation through deserialization). I've read on potential solution about annotating the setter and the getter differently but I'd like to keep this property final if possible. If not possible, I'd settle for a non-final property.
I would appreciate answers not suggesting custom serializer/deserializer, my code base is currently free of such and if the solution could be of minimal impact, that would be perfect. Again, I'm no Jackson expert so if what I'm asking is not possible I'll obviously accept alternative answers.
I've also read this thread on github but none of the suggested ways of implementation have actually been implemented at the moment.
Thanks
EDIT : to make things clearer
public class Foo {
private final String bar = null;
public String getBar() {
return bar;
}
#Override
public String toString() {
return bar;
}
}
public void testMethod() throws IOException {
String json = "{\"bar\":\"Value\"}";
ObjectMapper mapper = new ObjectMapper();
Foo foo = mapper.readValue(json, Foo.class);
System.out.println(foo); // should have a bar property set to "Value"
System.out.println(mapper.writeValueAsString(foo)); // should return an empty JSON object
}
I am not sure whether it is elegant solution but you can use MixIn feature. You have to create new interface which could look like below:
interface FooMixIn {
#JsonIgnore
String getBar();
}
Assume that your POJO looks like this:
class Foo {
private final String bar = null;
public String getBar() {
return bar;
}
#Override
public String toString() {
return bar;
}
}
Now you have to tell Jackson that you want to ignore this property:
String json = "{\"bar\":\"Value\"}";
System.out.println(json);
ObjectMapper deserializeMapper = new ObjectMapper();
deserializeMapper.addMixInAnnotations(Foo.class, FooMixIn.class);
System.out.println(deserializeMapper.readValue(json, Foo.class));
Above example prints:
{"bar":"Value"}
null
Without deserializeMapper.addMixInAnnotations(Foo.class, FooMixIn.class); line above program prints:
{"bar":"Value"}
Value
EDIT 1
If you want to achieve result like you showed you have to create two ObjectMappers and customize them. See below example:
String json = "{\"bar\":\"Value\"}";
ObjectMapper deserializerMapper = new ObjectMapper();
Foo foo = deserializerMapper.readValue(json, Foo.class);
System.out.println("Foo object: " + foo);
ObjectMapper serializerMapper = new ObjectMapper();
serializerMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
serializerMapper.addMixInAnnotations(Foo.class, FooMixIn.class);
System.out.println("JSON: " + serializerMapper.writeValueAsString(foo));
For serialization you have to use one instance and for deserialization you have to use another instance.
Starting with Jackson 2.6, a property can be marked as read- or write-only. It's simpler than hacking the annotations on both accessors (for non-final fields) and keeps all the information in one place. It's important to note that a final field is considered writable by default by Jackson.
However, it's not enough for a final field to allow deserialization, because you can't have a setter on that field: it needs to be set via the constructor, either directly or using a builder or another type that can be deserialized by Jackson. When using the constructor with the properties as parameters, you need to specify which parameter corresponds to which property, using #JsonProperty:
public class Foo {
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private final String bar;
public Foo(#JsonProperty("bar") String bar) {
this.bar = bar;
}
public String getBar() {
return prop;
}
}
Related
I tried deserialize following json to POJO.
{
"foo": {
"key1":"dummy",
"key2":"dummy"
},
"bar": {
"key1":"dummy",
"key2":"dummy",
"key3":"dummy"
},
"bazKey1":"dummy",
"bazKey2":"dummy",
"bazKey3":"dummy",
"bazKey4":"dummy"
// Many others....
}
You can see above strange baz properties...
But I want to treat baz as an object like foo and bar.
public class Pojo {
private Foo foo;
private Bar bar;
private Baz baz;
// Many others....
}
However, I just found poor solution which uses custom deserializer.
Poor solution
#Override
public Pojo deserialize(JsonParser p, DeserializationContext ctxt) throws Exception {
ObjectCodec codec = p.getCodec();
JsonNode node = codec.readTree(p);
Baz baz = new Baz.Builder()
.key1(node.get("bazKey1").textValue())
.key2(node.get("bazKey2").textValue())
.key3(node.get("bazKey3").textValue())
.key4(node.get("bazKey4").textValue())
.build();
// We have to write annoying (setter/constructor/builder) instead of below method.
// return codec.treeToValue(node, Pojo.class);
return new Pojo.Builder()
.foo(foo)
.bar(bar)
.baz(baz)
.other(other)
.other(other)
.other(other) // Many others...
.build();
}
This solution forces us to use annoying (setter/constructor/builder).
How to deserialize fields into object using jackson?
Additionally, this POJO is Immutable object.
The point of Jackson is that you're not in this situation. The very reason why you use Jackson at all is because when you have a Baz Java object, it's represented with a baz JSON property that contains an object, and when you have several string JSON properties, they are represented in Java with several String fields of the same name.
If that's not the situation you're in, then there is no reason to consider Jackson. Use any JSON parsing library, and build your Java objects from the JSON tree. You can use Java reflection to discover the Java class' fields/methods and set/call them based on the JSON properties' names.
I am trying to deserialize a object ref ($ref) using ObjectMapper.
public class Foo {
#JsonProperty("bar")
private Bar bar;
#JsonProperty("bar")
public Bar getBar() {
return bar;
}
#JsonProperty("bar")
public void setBar(Bar bar) {
this.bar = bar;
}
}
test.json
This is the json file I am trying to deserialize.
Is this is the correct way to refer to a object/json reference?
{
"bar": {"$ref": "/bar.json"}
}
Deserializer.java
ObjectMapper objectMapper = new ObjectMapper();
//load class
URL url = Deserializer.class.getClassLoader().getResource("test.json");
//deserialize
objectMapper.readValue(url, Foo.class);
the result creates a Foo pojo with additional property of "bar": ""$ref": "/bar.json"" rather than deserializing it. Do I need to implement the deserialize interface and manually deserialize the node?
Traditionally in Comp Sc. this problem is solved using what is known as "Pointer Swizzling".
This means that If you have an Object A that contains a reference to B and you want to serialize this structure (and then deserialize it), you would need to "unswizzle" the pointer to B to a "name" (an identifier that uniquely identifies the instance B) , write it to disk. When deserializing, you would then take that name, find the instance that it points to (B) and "swizzle" the name back to a proper pointer to B.
Now, in Java pointers are called references but it's the same.
Here's an example to illustrate:
originalA = { "id":"id_a", "ref_to_b": originalB}
originalB = { "id":"id_b" }
Applying unswizzling:
readyForSerializationA = { "id":"id_a", "ref_to_b": "id_b"}
readyForSerializationB = { "id": "id_b" }
followed by writing to store/reading back from store.
Applying swizzling:
deserializedB = { "id":"id_b" }
deserializedA = { "id": "id_a", "ref_to_b": deserializedB}
One possible way to do it for your case,is to deserialize all objects first, put them into an HashMap and in a second pass, look up the ObjectReference(s) from the various ObjectID(s) that you have in your JSON (swizzling).
Some further reading: https://en.wikipedia.org/wiki/Pointer_swizzling
You need to store {"$ref": "/bar.json"} this as a Map. That's the simplest way to store it.
Example:
public class Foo {
#JsonProperty("bar")
private Map<String, Bar> bar;
#JsonProperty("bar")
public Map<String, Bar> getBar() {
return bar.get("$ref");
}
#JsonProperty("bar")
public void setBar(Map<String, Bar> bar) {
this.bar = bar;
}
}
Only then it will get the value of $ref in Bar object. Otherwise, the data will be in incorrect format and Bar object will take the entire bar value into it.
Is it possible to have the #JsonProperty required dynamically set or set at call?
The reason behind this...
I'm generating json files which describes a schema and defines
What are the required fields for a new item
What are the required fields for an update to an item.
So, a creation requires only foo
and an update requires foo and bar
Can I make things so I can pass in something to say bar is now required?
or would I need to duplicate this code in order to have different settings for JsonProperty?
#JsonInclude(Include.NON_NULL)
public class Bean {
#JsonProperty(value="foo", required=false)
private FooProperty fooProperty;
#JsonProperty(value="bar", required=false)
private BarProperty barProperty;
//
public FooProperty getFooProperty() { return fooProperty; }
public void setFooProperty(FooProperty argFooProperty) {
this.fooProperty = argFooProperty
}
public BarProperty getBarProperty() { return barProperty; }
public void setFooProperty(BarProperty argBarProperty) {
this.barProperty = argBarProperty
}
}
You could solve this issue in couple of ways. First one would be as Franjavi suggested you could use a mixin.
Have a mixin class which will mark your foo as one of the ignored properties. In your main class mark both the fiends required and you can inject this mixin whenever this is an option field.
#JsonIgnoreProperties("foo")
public abstract class mixinClass {
}
You configure your mixin into your mapper as follows.
ObjectMapper mapper = new ObjectMapper();
mapper.addMixInAnnotations(Bean.class, mixinClass.class);
This is not always a working option whenever you are getting this response from a third party API where you might be serializing/deserializing using the default jackson mapper which are provided by them like Resttemplate.
In this case, instead of having the foo property, you can just included whatever the properties that are present in all the responses and handle the rest of the properties using #JsonAnyGetter and #JsonAnySetter. You can capture this in a map with key being your object name which is in this case foo. You need to have the following part in your parent node or whichever node that will encapsulates these optional properties.
#JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
#JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
#JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
Let me know if you need any further clarification!!
You can play wiht Mixins and the mapper:
public interface BarRequiredMixIn {
#JsonProperty(value="bar", required=true)
private BarProperty barProperty;
}
ObjectMapper mapper = new ObjectMapper();
// When you want that it is required
mapper.addMixInAnnotations(Bean.class, BarRequiredMixIn.class);
But I strongly recommend to avoid to use this kind of conditional restrictions with Jackson, since it is not designed for it, and it looks kind of confusing.
Based on the example you have given, setting the following in your POJO would be sufficient & Jackson would be able to figure out whether to deserialize it/not . All #JsonProperty are required by default, so with the code below, you would be able to achieve optional bar value in your Create/Update scenarios based on payload in the request
#JsonProperty(value="bar", required=false)
private Bar bar;
I'm using Jersey JAX-RS with Jackson (for serialization/deserialization) to implement a set of REST services. When a caller performs an update operation (ex. a PUT), I've generally followed the convention that null fields sent in the request are ignored when the target is updated. Only fields that were set to an actual value are updated.
However, I'd prefer it if I could differentiate between fields that were sent as null vs fields that weren't sent at all so I know to clear fields that were explicitly sent as null.
I can invent a way to accomplish this, but I'm wondering if there's anything available in the framework. It seems like a common requirement.
If you are using JSON POJO support (init parameter com.sun.jersey.api.json.POJOMappingFeature to true in web.config) then a simple solution is to have "a smart setter" on your POJO:
class MyBean {
private String foo;
private String bar;
private boolean fooSet;
public String getFoo() {
return this.foo;
}
public void setFoo(String foo) {
this.foo = foo;
this.fooSet = true;
}
public String getBar() {
return this.bar;
}
public void setBar(String bar) {
this.bar = bar;
}
public boolean isFooSet() {
return this.fooSet;
}
}
The Jackson will call the setter if the field is present (no matter the value) and will ignore it if the field is missing altogether.
For JAXB Based JSON support I don't know if the setter will ever be called so it might be necessary to write custom MessageBodyReader/MessageBodyWriters or a specialized form of JSONJAXBContext.
How do I tell Xstream to serialize only fields which are annotated explicitly and ignore the rest?
I am trying to serialize a hibernate persistent object and all proxy related fields get serialized which I don’t want in my xml.
e.g.
<createdBy class="com..domain.Users " reference="../../values/createdBy"/>
is not something I want in my xml.
Edit: I don’t think I made this question clear. A class may inherit from a base class on which I have no control (as in hibernate’s case) on the base class properties.
public class A {
private String ShouldNotBeSerialized;
}
public class B extends A {
#XStreamAlias("1")
private String ThisShouldbeSerialized;
}
In this case when I serialize class B, the base class field ShouldNotBeSerialized will also get serialized. This is not something I want. In most circumstances I will not have control on class A.
Therefore I want to omit all fields by default and serialize only fields for which I explicitly specify the annotation. I want to avoid what GaryF is doing, where I need to explicitly specify the fields I need to omit.
You can omit fields with the #XstreamOmitField annotation. Straight from the manual:
#XStreamAlias("message")
class RendezvousMessage {
#XStreamOmitField
private int messageType;
#XStreamImplicit(itemFieldName="part")
private List<String> content;
#XStreamConverter(SingleValueCalendarConverter.class)
private Calendar created = new GregorianCalendar();
public RendezvousMessage(int messageType, String... content) {
this.messageType = messageType;
this.content = Arrays.asList(content);
}
}
I can take no credit for this answer, just sharing what I have found. You can override the wrapMapper method of the XStream class to achieve what you need.
This link explains in detail: http://pvoss.wordpress.com/2009/01/08/xstream/
Here is the code you need if you don't want the explanation:
// Setup XStream object so that it ignores any undefined tags
XStream xstream = new XStream() {
#Override
protected MapperWrapper wrapMapper(MapperWrapper next) {
return new MapperWrapper(next) {
#Override
public boolean shouldSerializeMember(Class definedIn,
String fieldName) {
if (definedIn == Object.class) {
return false;
}
return super
.shouldSerializeMember(definedIn, fieldName);
}
};
}
};
You might want to do all your testing before you implement this code because the exceptions thrown by the default XStream object are useful for finding spelling mistakes.
There was already a ticket for the XStream people:
Again, this is by design. XStream is a serialization tool, not a data
binding tool. It is made to serialize Java objects to XML and back. It
will write anything into XML that is necessary to recreate an equal
object graph. The generated XML can be tweaked to some extend by
configuration for convenience, but this is already an add-on. What you
like to do can be done by implementing a custom mapper, but that's a
question for the user's list and cannot be handled here.
http://jira.codehaus.org/browse/XSTR-569
I guess the only direct way is to dive into writing a MapperWrapper and exclude all fields you have not annotated. Sounds like a feature request for XStream.