Jackson case changes in JSON serialization - java

I'm having some cases where Jackson is changing cases of fields in a POJO during serialization in ways I would not expect. I'm using the following block to do the serialization:
ObjectMapper mapper = new ObjectMapper()
String json = mapper.writeValueAsString(o)
I'm noticing that all of the fields which are prepended with 'v' are getting the next capital letter also lowercased. For example, for the POJO field as below:
vStatus1 = "3424522"
I see after serialization the following JSON field:
vstatus1="3424522"
This is mainly important to me because I'm using a switch to go between two different web services which should provide the same output map, but this hiccup is preventing me from doing it cleanly.

If you are using Jackson, you can set the #JsonProperty on the field's getter and change it to whatever you like...
#JsonProperty("vStatus1")
public String getvStatus1() {
return vStatus1;
}
// Produces: {"vStatus1":"3424522"}
#JsonProperty("VStatus1")
public String getvStatus1() {
return vStatus1;
}
// Produces: {"VStatus1":"3424522"}
I'm using a switch to go between two different web services which should provide the same output map
In addition to the above, perhaps consider switching on the lowercased version of your string...
switch(myString.toLowerCase()) {
case "foo": doSomething();
...
}

Related

Using Jackson Mixins to selectively ignore only provided properties

I have the following situation:
public class A {
private String someProperty;
private String anotherProperty;
public A() {}
// getter/setter
An ObjectMapper configuration as follows (enabled by default, but worth noting to get the point of the question across):
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true);
and an input JSON that looks as follows:
{
"someProperty": "someValue",
"anotherProperty":"anotherValue",
"unwantedProperty":"unwantedValue"
}
When deserializing this JSON using objectMapper.readValue(bytes, A.class), as expected, it gives me an exception:
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "unwantedProperty"
Now, what I want to do, is the following:
Only deserialize properties that are explicitly provided by A.class
Use a MixIn to ignore all unwanted, known properties. So for example, I know that unwantedProperty is part of the JSON, but I don't need it, so I want to ignore it.
Still raise an UnrecognizedPropertyException if a new, unknown property suddenly appears in my JSON.
The reason that I wish to use something like a MixIn class for this is that in reality, the input JSON has several dozens of fields. I would prefer not to clutter my A.class with dozens and dozens of unused properties with #JsonIgnore on them, so that it only contains the fields that I really want. If a new property unexpectedly does come along, I want to be forced to have a look at it.
I thought that I could allow this behaviour by using a MixIn as follows:
public abstract class AMixIn {
#JsonIgnore private String unwantedProperty;
together with:
objectMapper.addMixIn(A.class, AMixIn.class);
but this seemingly has no effect. I've also tried creating getters in AMixIn and giving those #JsonIgnore, but this also has no result.
Am I using MixIns incorrectly here? Is what I'm trying to do even possible (as described in the 3 points above)? Or, is there a better way to do this?
I've tried searching, but my use case is a bit esoteric, so I haven't had much luck.
Answer to question 1:
You can instruct Jackson to ignore unknown properties.
I tend to configure the ObjectMapper to ignore them,
here is some sample code:
private ObjectMapper mapper;
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
// This matches the Fuse Mapper configuration.
builder.featuresToDisable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
mapper = builder.build();
You can also annotate the class to ignore unknown properties.
Here is some sample code:
#jsonignoreproperties(ignoreunknown = true)
public class A
{
...
Note about question 2:
In order for the use-mixin-to-ignore-fields-in-json strategy to work,
the fields to be ignored must exist in the class.
In your case,
this means that class A must have a field "unwantedProperty" for the mixin to work correctly.
Direction to solve questions 2 and 3
You cannot use the MixIn feature of Jackson to solve either
question 2 or question 3.
Instead,
you will need to write a custom deserializer.
I suggest that you also use a custom Jackson annotation
that configures a list of ignored-unrecognized-fields
and have your custom deserializer only throw the exception
for unrecognized fields that are not part of the
ignored-unrecognized-fields list.
Well, shows how well I can search; as always, Jackson provides some way of doing whatever needs doing. In case it might help someone else:
There exists the #JsonIgnoreProperties annotation which, in addition to the ignoreUnknown property (that I already knew), supports a list of properties (via value()) to ignore during de-/serialization (which I did not know).
This is not quite the same as the intended solution above, but having these properties inside of #JsonIgnoreProperties({ ... }) in the class header instead of the class body is a good enough compromise for me.
So, the solution would be:
#JsonIgnoreProperties({"unwantedProperty"})
public class A {
// same as above...
}
Still, if there is a MixIn solution which can completely decouple these things, I'd still like to see it. I'll accept my own answer if nothing comes up in a few days.

JSON Binding #JsonbTypeDeserializer annotation ignored on enums?

I'm converting a JAXB application to JSON-B and I've run into an issue while trying to deserialize a Java enum using a custom JsonbDeserializer inside one of my tests.
The original JSON I need to deserialize contains ints referencing the enum's constants. Therefore my custom JsonbDeserializer needs to take the int and return the enum constant with the matching ordinal. It looks like this:
#JsonbTypeDeserializer(Region.RegionDeserializer.class)
public enum Region implements BaseEnum {
REGION_A,
REGION_B;
static final class RegionDeserializer implements JsonbDeserializer<Region> {
// deserialize() method returns REGION_A for 0 and REGION_B for 1.
}
}
Then I run it like this:
try (var jsonb = JsonbBuilder.create()) {
var result = jsonb.fromJson(text, Region.class);
} catch (final Exception ex) {
fail(ex);
}
Unfortunately, here's what I get back:
java.lang.IllegalArgumentException: No enum constant Region.1
at java.base/java.lang.Enum.valueOf(Enum.java:266)
at org.eclipse.yasson.internal.serializer.EnumTypeDeserializer.deserialize(EnumTypeDeserializer.java:40)
As you can see, RegionDeserializer is not used. Instead, the default enum deserializer is used. Looking into the JSON-B docs, I see I should register the deserializer manually:
JsonbConfig config = new JsonbConfig()
.withDeserializer(RegionDeserializer.class);
Jsonb jsonb = JsonbBuilder.create(config);
...
And when I do that, the code in fact works. But here's my question - what can I do to have the JsonbTypeDeserializer annotation registered automatically? Considering I have a lot of enums I need custom deserializers for, registering them manually really doesn't scale.
EDIT 1: I have tried to use #JsonbCreator-annotated static method instead, and the result was the same. The default enum deserializer was still used.
The JSON-B specification mentions both ways of registering the custom deserializer:
There are two ways how to register JsonbSerializer/JsonbDeserializer:
Using JsonbConfig::withSerializers/JsonbConfig::withDeserializers method;
Annotating a type with JsonbSerializer/JsonbDeserializer annotation.
The fact that the annotation does not work is a bug. I could reproduce this on Yasson 1.0.6, but not on Yasson 2.0.0-M1. Perhaps updating to the latest version solves your problem?

Jackson Deserializer - inject key:value pairs if not present in serialized data

I save the payload of events as JSON string into a database.
To only have one version of the events in code while still being able to read old events I want to "upcast" the serialized data before deserialization.
I thought about something like this:
public Object deserialize(String data, Class<?> clazz) throws IOException {
data = upcaster.upcast(data, clazz);
return objectMapper.readValue(data, clazz);
}
But this means I transform the string into some JSON object twice. Once for upcasting the data and once inside the standard jackson mapper.
Is there a way to customize jackson between building the json-object and building my event object?
Something like:
#override
updateData(clazz, jsonData) {
if(clazz.equals(SpecificEvent.class)) {
if(!jsonData.containsKey("addedInfo")) {
jsonData.put("addedInfo", "foo");
}
}
}
Alternatively, I could add my own deserializer that changes the given JSON data before calling the standard deserializer I guess?
What's the normal way of doing something like this with jackson?
One option is custom deserialization with a dedicated deserializer class (per your own class). You would then have to maintain the deserializer class to make sure that it provides any missing data.
It should work for a handful of classes, if you have a lot then there might be better ways.

Java Object to JSON Conversion

I have a Java object as below
public class Command {
private String cmd;
private Object data;
}
I want JSON Conversion of this Object to look as below
{"cmd":"getorder","data":{"when":"today"}}
How do I do this without changing the Class definition?
I know how to use GSON or Jackson library. I am having trouble assigning values to or initializing (Object) data above, so that it properly converts to {"when":"today"} when I use those libraries.
Thanks
You can try Gson library
it's very easy to use and it can do the reverse operation as well
Depending on your needs you might consider to add a handwritten json formatter for your class (of yourse this interferes with your demand to not change the class definition) but in fact it gives you max flexibility without 3rd party dependencies. If you strictly let all your Objects overwrite toString() to give json formatted string representation you could e.g.
String toString() {
StringBuffer result = new StringBuffer();
result.add("{ \"cmd\":" + this.cmd);
result.add(",");
result.add( \"data\":" + data.toString());
result.add("}");
return result.toString();
}
In case your need to not change the class definition appears more important than the mentioned advanteges there is a a nice json library avaialble on code.google.com named "simple json" ( https://code.google.com/p/json-simple/ ).

Canonicalizing Java bean property names

I have a bunch of third-party Java classes that use different property names for what are essentially the same property:
public class Foo {
public String getReferenceID();
public void setReferenceID(String id);
public String getFilename();
public void setFilename(String fileName);
}
public class Bar {
public String getRefID();
public void setRefID(String id);
public String getFileName();
public void setFileName(String fileName);
}
I'd like to be able to address these in a canonicalized form, so that I can treat them polymorphically, and so that I can do stuff with Apache BeanUtils like:
PropertyUtils.copyProperties(object1,object2);
Clearly it would be trivial to write an Adapter for each class ...
public class CanonicalizedBar implements CanonicalizedBazBean {
public String getReferenceID() {
return this.delegate.getRefID();
}
// etc.
}
But I wonder is there something out there more generalized and dynamic? Something that would take a one-to-many map of property name equivalences, and a delegate class, and produce the Adapter?
I've never used it, but I think you're looking for Dozer:
Dozer is a Java Bean to Java Bean mapper that recursively copies data
from one object to another. Typically, these Java Beans will be of
different complex types.
Dozer supports simple property mapping, complex type mapping,
bi-directional mapping, implicit-explicit mapping, as well as
recursive mapping. This includes mapping collection attributes that
also need mapping at the element level.
Dozer not only supports mapping between attribute names, but also
automatically converting between types. Most conversion scenarios are
supported out of the box, but Dozer also allows you to specify custom
conversions via XML.
First Option is Dozer.
Second option is Smooks Framework
with a tweak. It will be beneficial to use Smook's Graphical mapper.
Another option would be XStream with custom Mapper.
maybe something like that:
public class CanonicalizedBar implements CanonicalizedBazBean {
public String getReferenceID() {
Method m = this.delegate.getClass().getDeclaredMethod("getReferenceID");
if(m == null)
m = this.delegate.getClass().getDeclaredMethod("getRefID");
...
return m.invoke();
}
// etc.
}
Although, I personally have never used it. I noticed that a project called orika is noted as having the best performance and the ability to automatically understand many such mappings.
At any rate it also supports custom mappings and uses generated code to implicitly define the adapters.
You can also define a custom mapper, that is if you know how to canonize the member names you can use that knowledge to build a mapping that is true for all your objects. for instance:
DefaultFieldMapper myDefaultMapper = new DefaultFieldMapper() {
public String suggestMapping(String propertyName, Type<?> fromPropertyType) {
// split word according to camel case (apache commons lang)
String[] words= StringUtils.splitByCharacterTypeCamelCase(propertyName);
if(words[0].length() > 6) {
// trim first camel-cased word of propery name to 3 letters
words[0]= words[0].substring(0,2);
return StringUtils.join(words);
} else {
// remains unchanged
return propertyName;
}
}
}
mapperFactory.registerDefaultFieldMapper(myDefaultMapper );
I haven't done much with it but you may be able to use Aspect Oriented Programming to do this.
What you should be able to do I think is add a method to each of the classes that internally calls the real method. See this article about half way down it talks about mixins.
AspectJ is probably the most popular implementation.

Categories

Resources