What is the best and easiest way to instantiate beans in custom way (not by calling default constructor) while deserializing from JSON using Jackson library? I found that there is JsonDeserializer interface that I could implement, but I'm not too sure how to wire it all together into the ObjectMapper.
UPDATE #1: I think some more details is required for my question. By default Jackson's deserializer uses default constructor to crete beans. I'd like to be able to implement instantiation of the bean by calling external factory. So what I need is just a class of the bean that needs to be instantiated. The factory will return instance that can then be provided to Jackson for property population and so on.
Please note that I'm not concerned about creation of simple/scalar values such as strings or numbers, only the beans are in the area of my interest.
Some things that may help...
First, you can use #JsonCreator to define alternate constructor to use (all arguments must be annotated with #JsonProperty because bytecode has no names), or a static factory. It would still be part of value class, but can help support immutable objects.
Second, if you want truly custom deserialization process, you can check out https://github.com/FasterXML/jackson-docs/wiki/JacksonHowToCustomSerializers which explains how to register custom deserializers.
One thing that Jackson currently misses is support for builder-style objects; there is a feature request to add support (and plan is to add support in future once developers have time).
You put the deserializer on the Java object you want to get the json mapped into:
#JsonDeserialize(using = PropertyValueDeserializer.class)
public class PROPERTY_VALUE implements Serializable{
and then in the Deserializer you can e.g. do:
#Override
public PROPERTY_VALUE deserialize(JsonParser jsonParser,
DeserializationContext deserializationContext)
throws IOException, JsonProcessingException {
String tmp = jsonParser.getText(); // {
jsonParser.nextToken();
String key = jsonParser.getText();
jsonParser.nextToken();
String value = jsonParser.getText();
jsonParser.nextToken();
tmp = jsonParser.getText(); // }
PROPERTY_VALUE pv = new PROPERTY_VALUE(key,value);
return pv;
}
If you don't want to use annotations, you need to pass the mapper a DeserializerProvider that can provide the right deserializer for a given type.
mapper.setDeserializerProvider(myDeserializerProvider);
For the constructors - of course you can generate the target class by calling a factory within the deserializer:
String value = jsonParser.getText();
jsonParser.nextToken();
tmp = jsonParser.getText(); // }
MyObject pv = MyObjectFactory.get(key);
pv.setValue(value);
return pv;
}
But then I may have misunderstood your update
Related
I have a class that needs to be serialized
public class Abc
{
private long age;
private JaxBElement<Foo> fooWrapper;
// other properties
}
The expected output JSON is
{
"age": 24,
"my_own_key": "my_own_value" // the key should not be "fooWrapper"
A constraint is that the original class Abc cannot be modified since it is generated out of xjc and I don't want to explore custom class using bindings yet.
I have tried custom serializers, bean modifiers etc. for the JaxBElement and all of them allow me to control the serialization. But they work at the VALUE of the property only. They don't allow me to change stuff at the "KEY-VALUE" level. This is the crux of the question. The key is already written out for the property before the custom serializer is invoked to control the value.
E.g. My custom serializer is invoked only after the Jackson system has emitted out the key
"fooWrapper": // now for the value part, let me invoke the custom serializer
So the output JSON always contains the "fooWrapper" key.
{ "fooWrapper": { "any-key": "any-value" } }
// the fooWrapper is already emitted out. That is what needs to be controlled.
My ask is to control the serialization at a higher level, such that both the key and value can be controlled. So when class Abc is being serialized, the fooWrapper property should not be written as a key at all and some custom serializer should be invoked.
Another constraint is that there are several classes like Abc which may have such JaxBElement. It is not known ahead of time. So there needs to be a generic way to attach the custom serializer.
The pseudo ask is really that we be able to attach a custom serializer to any class which has a property that matches a pattern such that the serializer can control the name of the property (or the whole key-value blob) written out.
Also, the problem is not specific to JaxBElement per se. It could be any property. The problem is more about controlled serialization INCLUDING the key being written out.
Maybe you just use the incorrect kind of Serializer. This post, although a bit old should show you how to do what you want with StdSerializer.
This kind of serializer allows you to control both the key and the value.
If you want to control serialisation of key-value pair you need to register custom serialiser not only for JaxBElement<Foo> fooWrapper but also for Abc class to change a key value.
Since it is not a generic solution you can also try to create MixIn class or interface and provide extra configuration:
interface MixInA {
#JsonSerialize(using = JAXBElementJsonSerializer.class)
#JsonProperty("newProperty")
JAXBElement<Foo> getFooWrapper();
}
See also:
What is equivalent code settings for #JSonIgnore annotation?
Make Jackson serializer override specific ignored fields
Downside of this solution is you have to find all types for which you have to register MixIn class or interface. In case fields are different you need to create many different getters or many different MixIn interfaces to cover them all.
So, probably them most flexible solution would be to implement custom com.fasterxml.jackson.databind.AnnotationIntrospector and for given type you can return custom serialiser and custom name. Simple example:
class DynamicJaxbAnnotationIntrospector extends AnnotationIntrospector {
#Override
public Version version() {
return new Version(1, 0, 0, "Dynamic JaxbElement", "your.package", "jackson.dynamic.jaxb");
}
#Override
public Object findSerializer(Annotated am) {
if (am.getRawType() == JAXBElement.class) {
return new JAXBElementJsonSerializer();
}
return super.findSerializer(am);
}
#Override
public PropertyName findNameForSerialization(Annotated a) {
if (a.getRawType() == JAXBElement.class) {
return new PropertyName("newProperty");
}
return super.findNameForSerialization(a);
}
}
See also below article how to use it:
How to serialise Enums as both Object Shape and default string?
I am trying to deserialize an Object from a Map(String, Object) via FasterJackson's ObjectMapper.convertValue(map, ComplexRecord.class) method.
My destination object contains many other objects within it. The only problematic object is the embedded "AnyObject" object that uses FasterJackson's (Jackson's) AnyGetter/AnySetter methods.
The AnyObject instance works for every other use case with FasterJackson except, at the moment, for this one where it involves a more "ComplexRecord" deserialization.
This is what it looks like:
#Data
public class ComplexRecord {
private String id;
private AnyObject data;
private String status;
private Instant createdDateTime;
}
#Data
public class AnyObject {
#Getter(AccessLevel.NONE)
#Setter(AccessLevel.NONE)
private final Map<String, Object> data;
public AnyObject() {
this(new LinkedHashMap<>());
}
public AnyObject(Map<String, Object> sourceData) {
this.data = new LinkedHashMap<>(sourceData);
}
#JsonAnySetter
public void setData(String name, Object value) {
this.data.put(name, value);
}
#JsonAnyGetter
public Map<String, Object> getData() {
return Collections.unmodifiableMap(this.data);
}
}
When I try to use ObjectMapper.convertValue(map, ComplexRecord.class) it fails to deserialize the "data" field due to this error:
Cannot construct instance of `AnyObject`
(although at least one Creator exists): no String-argument
constructor/factory method to deserialize from String value
('{"id":"123","v":"anything"}')
at [Source: UNKNOWN; line: -1, column: -1]
I'd like to find a very clean workaround for this. This issue seems to stem from the fact that I am using the ObjectMapper.convertValue method from a complex source of Map where the key "data" is originally available as a String. If I perform a similar operation but with ObjectMapper.readValue() instead of ObjectMapper.convertValue() then the deserialization into AnyObject works just fine.
Since I do not have the luxury of changing the source object of Map into something that would work with ObjectMapper.readValue() method I may be left with only a few choices.
One option I found is using FasterJackson's Custom Deserializer. The only catch I see is that there is no clear way to access the internal ObjectMapper that is provided to the Deserializer. When I debug the deserializer, I do see that JsonParser.getCodec() is the ObjectMapper, but even when trying to do a readValue within the the custom Deserializer the deserialization fails with the same error. i.e.
AnyObject value = jsonParser.getCodec().readValue(p, AnyObject.class);
However following set of calls work just fine:
String stringValue = jsonParser.getCodec().readValue(p, String.class);
AnyObject anyObject = objMapper.readValue(stringValue, AnyObject.class);
Other than this being a 2 step process to deserialize, rather than a 1 step process; the only other issue is that I do not have a clean way to use the "objMapper" (ObjectMapper) instance I am referring to above without casting the codec into an ObjectMapper instance.
This seems like a hack to me but I'd like to get your thoughts and also see if there any other friendlier solution available.
There are a few more thoughts / options but I'd like to get an unbiased opinion on this to see if there is a much simpler way of handling this type of "complex" conversion.
The most ideal outcome is to force coercion of the String into my desired type -- AnyObject. Via annotation or some very simple strategy.
But if I do have to deal with this via a Custom Deserializer, my preference would be to be able to get the handle to the in-process ObjectMapper in a clean way.
Thoughts?
Tried a few things and landed on the following by implementing a custom FasterJackson deserializer:
JsonNode node = jsonParser.readValueAsTree();
AnyObject val = jsonParser.getCodec().getFactory().createParser(node.asText().getBytes()).readValueAs(AnyObject.class);
I am still open to solutions that may be simpler or lean towards better performance with less intermediate garbage generation during parsing.
When deserializing a variety of JSON messages, I want to provide a default value for attributes of a certain type. It is generally suggested to simply specify the value in the Class, but this is error-prone if you have to do this across many Classes. You might forget one and end up with null instead of a default value. My intention is to set every property that is an Optional<T> to Optional.absent. Since null is exactly what Optional is trying to eliminate, using them with Jackson has proven to be frustrating.
Most features of Jackson that allow you to customize the deserialization process focus on the JSON that is the input, not around the process of instantiating the Object that you are deserializing into. The closest I seem to be getting to a general solution is by building my own ValueInstantiator, but there are two remaining issues I have:
how do I make it only instantiate Optional as absent but not interfere with the rest of the instantiation process?
how do I wire the end result into my ObjectMapper?
UPDATE: I want to clarify that I am looking for a solution that does not involve modifying each Class that contains Optional's. I'm opposed to violating the DRY principle. Me or my colleagues should not have to think about having to do something extra every time we add Optional's to a new or existing Class. I want to be able to say, "make every Optional field in every Class I deserialize into, pre-filled with Absent", only once, and be done with it.
That means the following are out:
abstract parent class (need to declare)
custom Builder/Creator/JsonDeserializer (needs annotation on each applicable class)
MixIn's? I tried this, combined with reflection, but I don't know how to access the Class I'm being mixed into...
Specifically for java.lang.Optional, there is a module by the Jackson guys themselves: https://github.com/FasterXML/jackson-datatype-jdk8
Guava Optional is covered by https://github.com/FasterXML/jackson-datatype-guava
It will create a Optional.absent for null's, but not for absent JSON values :-(.
See https://github.com/FasterXML/jackson-databind/issues/618 and https://github.com/FasterXML/jackson-datatype-jdk8/issues/2.
So you're stuck with initializing your Optionals just as you should initialize collections. It is a good practice, so you should be able to enforce it.
private Optional<Xxx> xxx = Optional.absent();
private List<Yyy> yyys = Lists.newArrayList();
You can write a custom deserializer to handle the default value. Effectively you will extend the appropriate deserializer for the type of object you are deserializing, get the deserialized value, and if it's null just return the appropriate default value.
Here's a quick way to do it with Strings:
public class DefaultStringModule extends SimpleModule {
private static final String NAME = "DefaultStringModule";
private static final String DEFAULT_VALUE = "[DEFAULT]";
public DefaultStringModule() {
super(NAME, ModuleVersion.instance.version());
addDeserializer(String.class, new DefaultStringDeserializer());
}
private static class DefaultStringDeserializer extends StdScalarDeserializer<String> {
public DefaultStringDeserializer() {
super(String.class);
}
public String deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException, JsonProcessingException {
String deserialized = jsonParser.getValueAsString();
// Use a default value instead of null
return deserialized == null ? DEFAULT_VALUE : deserialized;
}
#Override
public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException {
return deserialize(jp, ctxt);
}
}
}
To use this with an ObjectMapper you can register the module on the instance:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new DefaultStringModule());
To handle default values for fields not present in the JSON, I've typically seen this done through the use of a builder class that will construct the class using the values supplied and add any default values for the missing fields. Then, on the deserialized class (e.g. MyClass), add a #JsonDeserialize(builder = MyClass.Builder.class) annotation to indicate to Jackson to deserialize MyClass by way of the builder class.
Your value object should initialize these values as absent. That's the way to ensure that default values have no nulls. Guava module's Optional handler really should only deserialize them as "absent" (even with explicit JSON nulls), and never as nulls, with later versions.
But since Jackson only operates on JSON properties that exist (and not on things that could exist but do not), POJO still needs to have default absent assignment.
I am trying to generate documentation for existing services. Its a bunch of rest services. What I would like to do is to simply create a new tag in xdoclet, that new tag will have a parameter. something like
#JSONInputMessage("com.foo.bar.input")
#JSONOutputMessage("com.foo.bar.output")
the xdoclet will then go to that class, initialize it, dump it into Jackson to convert it to json, and then copy the resulting json into the javadoc.
All of this is simple enough. My problem is i need a way to take in an object, then walk the fields all the way down, initializing the objects so they actually show up in the json dump. Does anyone have an easy way to reflect thru an object and initialize all objects?
Podam initializes a java object tree with random data, but you may define a strategy or use attributes to decide the values that are set.
Simple example:
PodamFactory factory = new PodamFactoryImpl(); //This will use the default Random Data Provider Strategy
Pojo myPojo = factory.manufacturePojo(Pojo.class);
Or with a strategy:
DataProviderStrategy strategy = new MyDataProviderStrategy();
PodamFactory factory = new PodamFactoryImpl(strategy);
Pojo myPojo = factory.manufacturePojo(Pojo.class);
Or with attributes:
#PodamStrategyValue(PostCodeStrategy.class)
private String postCode;
and then you define the strategy class for that single attribute:
public class PostCodeStrategy implements AttributeStrategy<String> {
public String getValue() throws PodamMockeryException {
...
}
}
Would this work for you?
Long story short, one of my entities has a GeometryCollection that throws an exception when you call "getBoundary" (the why of this is another book, for now let's say this is the way it works).
Is there a way I can tell Jackson not to include that specific getter? I know I can use #JacksonIgnore when I do own/control the code. But this is not case, jackson ends reaching this point through continuous serialization of the parent objects. I saw a filtering option in jackson documentation. Is that a plausible solution?
Thanks!
You can use Jackson Mixins. For example:
class YourClass {
public int ignoreThis() { return 0; }
}
With this Mixin
abstract class MixIn {
#JsonIgnore abstract int ignoreThis(); // we don't need it!
}
With this:
objectMapper.getSerializationConfig().addMixInAnnotations(YourClass.class, MixIn.class);
Edit:
Thanks to the comments, with Jackson 2.5+, the API has changed and should be called with objectMapper.addMixIn(Class<?> target, Class<?> mixinSource)
One other possibility is, if you want to ignore all unknown properties, you can configure the mapper as follows:
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Using Java Class
new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
Using Annotation
#JsonIgnoreProperties(ignoreUnknown=true)
Annotation based approach is better. But sometimes manual operation is needed. For this purpose you can use without method of ObjectWriter.
ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
ObjectWriter writer = mapper.writer().withoutAttribute("property1").withoutAttribute("property2");
String jsonText = writer.writeValueAsString(sourceObject);
Mix-in annotations work pretty well here as already mentioned. Another possibility beyond per-property #JsonIgnore is to use #JsonIgnoreType if you have a type that should never be included (i.e. if all instances of GeometryCollection properties should be ignored). You can then either add it directly (if you control the type), or using mix-in, like:
#JsonIgnoreType abstract class MixIn { }
// and then register mix-in, either via SerializationConfig, or by using SimpleModule
This can be more convenient if you have lots of classes that all have a single 'IgnoredType getContext()' accessor or so (which is the case for many frameworks)
I had a similar issue, but it was related to Hibernate's bi-directional relationships. I wanted to show one side of the relationship and programmatically ignore the other, depending on what view I was dealing with. If you can't do that, you end up with nasty StackOverflowExceptions. For instance, if I had these objects
public class A{
Long id;
String name;
List<B> children;
}
public class B{
Long id;
A parent;
}
I would want to programmatically ignore the parent field in B if I were looking at A, and ignore the children field in A if I were looking at B.
I started off using mixins to do this, but that very quickly becomes horrible; you have so many useless classes laying around that exist solely to format data. I ended up writing my own serializer to handle this in a cleaner way: https://github.com/monitorjbl/json-view.
It allows you programmatically specify what fields to ignore:
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(JsonView.class, new JsonViewSerializer());
mapper.registerModule(module);
List<A> list = getListOfA();
String json = mapper.writeValueAsString(JsonView.with(list)
.onClass(B.class, match()
.exclude("parent")));
It also lets you easily specify very simplified views through wildcard matchers:
String json = mapper.writeValueAsString(JsonView.with(list)
.onClass(A.class, match()
.exclude("*")
.include("id", "name")));
In my original case, the need for simple views like this was to show the bare minimum about the parent/child, but it also became useful for our role-based security. Less privileged views of objects needed to return less information about the object.
All of this comes from the serializer, but I was using Spring MVC in my app. To get it to properly handle these cases, I wrote an integration that you can drop in to existing Spring controller classes:
#Controller
public class JsonController {
private JsonResult json = JsonResult.instance();
#Autowired
private TestObjectService service;
#RequestMapping(method = RequestMethod.GET, value = "/bean")
#ResponseBody
public List<TestObject> getTestObject() {
List<TestObject> list = service.list();
return json.use(JsonView.with(list)
.onClass(TestObject.class, Match.match()
.exclude("int1")
.include("ignoredDirect")))
.returnValue();
}
}
Both are available on Maven Central. I hope it helps someone else out there, this is a particularly ugly problem with Jackson that didn't have a good solution for my case.
If you want to ALWAYS exclude certain properties for any class, you could use setMixInResolver method:
#JsonIgnoreProperties({"id", "index", "version"})
abstract class MixIn {
}
mapper.setMixInResolver(new ClassIntrospector.MixInResolver(){
#Override
public Class<?> findMixInClassFor(Class<?> cls) {
return MixIn.class;
}
#Override
public ClassIntrospector.MixInResolver copy() {
return this;
}
});
One more good point here is to use #JsonFilter.
Some details here Feature: JSON Filter