How to write custom serializer and deserializer in Jackson? - json

I have a class that has more than a dozen properties. For most of the properties of primitive type, I hope to use the default BeanSerializer and BeanDeserializer or whatever to reduce the cumbersome code I need to write. For other properties of custom and array types, I want to do some custom serializer/deserializer. Note that I am not able to change the underlying JSON string. But I have full access to the android code. I am using Jackson 1.7.9/Ektorp 1.1.1.
shall I subclass BeanDeserializer? I am having trouble with that. It expects a default constructor with no parameters but I don't know how to call the super constructor.
class MyType{
// a dozen properties with primitive types String, Int, BigDecimal
public Stirng getName();
public void setName(String name);
// properties that require custom deserializer/serializer
public CustomType getCustom();
public void setCustom(CustomType ct);
}
class MyDeserializer extends BeanDeserialzer{
// an exception is throw if I don't have default constructor.
// But BeanDeserializer doesn't have a default constructor
// It has the below constructor that I don't know how to fill in the parameters
public MyDeserializer(AnnotatedClass forClass, JavaType type,
BeanProperty property, CreatorContainer creators,
BeanPropertyMap properties,
Map<String, SettableBeanProperty> backRefs,
HashSet<String> ignorableProps, boolean ignoreAllUnknown,
SettableAnyProperty anySetter) {
super(forClass, type, property, creators, properties, backRefs, ignorableProps,
ignoreAllUnknown, anySetter);
}
#Override
public Object deserialize(JsonParser jp, DeserializationContext dc, Object bean)
throws IOException, JsonProcessingException {
super.deserialize(jp, dc, bean);
MyType c = (MyType)bean;
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readValue(jp, JsonNode.class);
// Use tree model to construct custom
// Is it inefficient because it needs a second pass to the JSON string to construct the tree?
c.setCustom(custom);
return c;
}
}
I searched Google but couldn't find any helpful examples/tutorial. If anyone can send me some working examples that would be great! Thanks!

To sub-class BeanSerializer/-Deserializer, you would be better off using a more recent version of Jackson, since this area has been improved with explicit support via BeanSerializerModifier and BeanDeserializerModifier, which can alter configuration of instances.
But just to make sure, you can also specify custom serializer/deserializer to just be used on individual properties, like so:
class Foo {
#JsonSerialize(using=MySerializer.class)
public OddType getValue();
}

Related

How can I define float/double numbers precision in json from Spring MVC?

How can I set Spring MVC to serialize float or double numbers to json with a limited number of decimals?
If you are serializing from bean, the easiset would be to write a custom deserializer, e.g.
public class FloatSerializer extends JsonSerializer<Float> {
#Override
public void serialize(Float value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
if (null == value) {
jgen.writeNull();
} else {
final String serializedValue = null;
// do your customization here
jgen.writeNumber(serializedValue);
}
}
}
and apply it to the field, e.g.
#JsonSerialize(using = FloatSerializer.class)
public Float getFloatField()
or simply convert the value in the setter of the property if its a one time conversion that works for you
-- Update with respect to the comment
If you want to apply globally than you'll have to use a custom jackson object mapper in your spring mvc and follow the guide for adding modules http://wiki.fasterxml.com/JacksonFeatureModules, the gist is along the following lines
ObjectMapper mapper = new ObjectMapper();
// basic module metadata just includes name and version (both for troubleshooting; but name needs to be unique)
SimpleModule module = new SimpleModule("EnhancedDatesModule", new Version(0, 1, 0, "alpha"));
// functionality includes ability to register serializers, deserializers, add mix-in annotations etc:
module.addSerializer(MyBean.class, new MyCustomSerializer());
module.addDeserializer(MyBean.class, new MyCustomSerializer());
// and the magic happens here when we register module with mapper:
mapper.registerModule(module);

Jackson: is it possible to replace the serializer set with #JsonSerialize annotation (e.g. with ObjectMapper)?

Quick question: is it possible to override #JsonSerialize annotation (using attribute) with ObjectMapper?
I'm have spring-security-oauth2 integrated and I want to customize the way OAuth2Exception is serialized to JSON format. The problem is that this class uses
#JsonSerialize(using = OAuth2ExceptionJackson2Serializer.class)
I tried registering custom serializer with:
SimpleModule module = new SimpleModule()
module.addSerializer(OAuth2Exception, new JsonSerializer<OAuth2Exception>() {
#Override
void serialize(OAuth2Exception value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
jgen.writeString('{"test":"test"}')
}
})
ObjectMapper objectMapper = new ObjectMapper()
objectMapper.registerModule(module)
but it didn't work - the serializer set with #JsonSerialize is used instead of the custom one.
Is there any other way to replace the serializer set with #JsonSerialize?
PS: the sample code is written in groovy
For such case Jackson has a mechanism called mix-in annotations.
You can create a class that overrides initial annotations.
#JsonSerialize(using=MySerializer.class)
public static abstract class OAuth2ExceptionMixIn {
}
Then register it in the object mapper:
objectMapper.addMixIn(OAuth2Exception.class, OAuth2ExceptionMixIn.class);
And that's it. Now Jackson should use your MySerializer instead of the initial OAuth2ExceptionJackson2Serializer
.

How to use default serialization in a custom JsonConverter

I have a complex object graph that I am serializing/deserializing with Json.NET. Some of the objects derive from an abstract class, so in order for the deserialization to work properly, I needed to create a custom JsonConverter. Its only role is to select the appropriate concrete implementation of the abstract class at deserialization-time and allow Json.NET to continue on its way.
My problem comes when I want to serialize. I don't need to do anything custom at all. I want to get exactly the same behavior as I would get using JsonConvert.SerializeObject with no custom JsonConverter.
However, since I'm using the custom JsonConverter class for my deserialization needs, I'm forced to supply a WriteJson implementation. Since WriteJson is abstract, I can't just call base.WriteJson, but I want to do essentially that. So my question is, what do I put in that method to get the plain-Jane, default behavior? In other words:
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
// What goes here to get default processing?
}
In your custom JsonConverter, override CanWrite and return false:
public override bool CanWrite { get { return false; } }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
Then you can just throw an exception from WriteJson, since it won't get called.
(Similarly, to get default behavior during deserialization, override CanRead and return false.)
Note that the same approach can be used for JsonConverter<T> (introduced in Json.NET 11.0.1) since it is just a subclass of JsonConverter that introduces type-safe versions of ReadJson() and WriteJson().

How to serialize in jackson json null string to empty string

I need jackson json (1.8) to serialize a java NULL string to an empty string. How do you do it?
Any help or suggestion is greatly appreciated.
Thanks
See the docs on Custom Serializers; there's an example of exactly this, works for me.
In case the docs move let me paste the relevant answer:
Converting null values to something else
(like empty Strings)
If you want to output some other JSON value instead of null (mainly
because some other processing tools prefer other constant values --
often empty String), things are bit trickier as nominal type may be
anything; and while you could register serializer for Object.class, it
would not be used unless there wasn't more specific serializer to use.
But there is specific concept of "null serializer" that you can use as
follows:
// Configuration of ObjectMapper:
{
// First: need a custom serializer provider
StdSerializerProvider sp = new StdSerializerProvider();
sp.setNullValueSerializer(new NullSerializer());
// And then configure mapper to use it
ObjectMapper m = new ObjectMapper();
m.setSerializerProvider(sp);
}
// serialization as done using regular ObjectMapper.writeValue()
// and NullSerializer can be something as simple as:
public class NullSerializer extends JsonSerializer<Object>
{
public void serialize(Object value, JsonGenerator jgen,
SerializerProvider provider)
throws IOException, JsonProcessingException
{
// any JSON value you want...
jgen.writeString("");
}
}

Jackson is adding redundant data to POJO and then can't read it back

I am using Jackson in order to send data in JSON type between a client a server.
I am trying to use Jackson's full binding feature and I am applying it over a standard POJO.
The problem is that Jackson seem to add redundant data during marshaling on the server so when I try to unmarshall it back to the POJO on the client side I'm getting an error.
Here's an excerpt of the Jackson String:
{"_class":"com.mycoomp.MyObject","_id":{"time":1300314145000,"new":false,"machine":1652794940,"inc":-510750341},"language":"","type".....
MyObject contains "language" and "type" but it it doesn't contain “time”, “new” and “machine” that are not part of it but on the client side i'm getting this error:
Unrecognized field "time" (Class org.bson.types.ObjectId), not marked as ignorable at [Source: java.io.StringReader#1c56c60; line: 1, column: 102] (through reference chain: com.mycomp.MyObject["_id"]->org.bson.types.ObjectId["time"])
Any ideas...?
The solution is to provide a custom serializer/deserializer for ObjectId:
public class ObjectIdMapping {
public static class ObjectIdSerializer extends JsonSerializer<ObjectId>{
#Override
public void serialize(ObjectId id, JsonGenerator json,
SerializerProvider provider) throws IOException,
JsonProcessingException {
json.writeString(id.toString());
}
}
public static class ObjectIdDeerializer extends JsonDeserializer<ObjectId>{
#Override
public ObjectId deserialize(JsonParser jp, DeserializationContext context)
throws IOException, JsonProcessingException {
if (!ObjectId.isValid(jp.getText())) throw context.mappingException("invalid ObjectId " + jp.getText());
return new ObjectId(jp.getText());
}
}
}
And register them as any of the methods described in the documentation. For example, add in your POJO:
#JsonSerialize(using = ObjectIdMapping.ObjectIdSerializer.class)
#JsonDeserialize(using = ObjectIdMapping.ObjectIdDeerializer.class)
public ObjectId od;
You need to give type definitions for types you are serializing. Jackson does not add any entries that are not discoverable from objects (via getters, public fields, or explicitly annotated); except in cases where you add #JsonTypeInfo annotation to also add type identifier.
So maybe object you are serializing has more public fields that will be serialized?
I've just come across this as I had the same problem. Seems like a job for the mongo-jackson-mapper
I would also advise taking infrastructural classes such as ObjectId out of your domain.