Jackson vs Gson for simple deserialisation - java

For parsing JSON like this twitter API users/show response I've been using Jackson and Gson Java libraries as candidates to do this work. I'm only interested in a small subset of properties of the JSON so Gson was nice because of its very concise syntax but I'm losing an internal battle to continue to use Gson as Jackson is already used elsewhere in our application and it has documented better performance (which I concede are both good reasons to lose Gson).
For a POJO like
public class TwitterUser {
private String id_str;
private String screen_name;
public String getId_str() {
return id_str;
}
public void setId_str(String id_str) {
this.id_str = id_str;
}
public String getScreen_name() {
return screen_name;
}
public void setScreen_name(String screen_name) {
this.screen_name = screen_name;
}
}
The only code for Gson needed to build this is one line,
TwitterUser user = new Gson().fromJson(jsonStr, TwitterUser.class);
That's pretty nice to me; scales well and is opt-in for the properties you want. Jackson on the other hand is a little more laborious for building a POJO from selected fields.
Map<String,Object> userData = new ObjectMapper().readValue(jsonStr, Map.class);
//then build TwitterUser manually
or
TwitterUser user = new ObjectMapper().readValue(jsonStr, TwitterUser.class);
//each unused property must be marked as ignorable. Yikes! For 30 odd ignored fields thats too much configuration.
So after that long winded explanation, is there a way I can use Jackson with less code than is demonstrated above?

With Jackson 1.4+ you can use the class-level #JsonIgnoreProperties annotation to silently ignore unknown fields, with ignoreUnknown set to true.
#JsonIgnoreProperties(ignoreUnknown = true)
public class TwitterUser {
// snip...
}
http://wiki.fasterxml.com/JacksonAnnotations
http://wiki.fasterxml.com/JacksonHowToIgnoreUnknown

Related

Jackson: How do I post-process JsonNode during serialization?

I am attempting to implement the HL7 FHIR spec's assertion that JSON representing a FHIR model will not have empty objects nor empty arrays. For the sake of not making the lives of my consumers any harder, I'm not strictly enforcing this during deserialization, but I want to ensure the serialized JSON produced by my library conforms as specified. I am using Java and Jackson ObjectMapper to serialize Objects into JSON. My understanding from writing a custom serializer is that the Object is at one point represented as JsonNode, regardless of what you are converting to.
What I would like to do is intercept the JsonNode as it exits the serializer, make some adjustments to it (find and remove empty arrays and objects), and then let it continue on its way. I need to do this in an environment where I can't tweak the ObjectMapper, because I don't have access to it. And further, the complex hierarchy of models in this library use Jackson's default serialization with annotations etc. heavily, and I cannot eliminate this.
If I go the route of defining a custom serializer for the base type, let's say "Resource", then I have a problem, because I still need the original serializer's output in order to generate my modified output. And further, that needs to accommodate any custom serializers that may already exist on various types within the model.
I got pretty far with the above option using https://www.baeldung.com/jackson-call-default-serializer-from-custom-serializer and the last option, implementing BeanSerializerModifier, but I ran into the issue where I can't control the ObjectMapper that my library consumers use.
Example POJOs (Using Lombok for accessors):
#Data
#JsonInclude(JsonInclude.Include.NON_EMPTY)
#JsonIgnoreProperties(ignoreUnknown = true)
abstract class Resource {
private FhirString id;
private List<Extension> extension;
#JsonProperty(access = JsonProperty.Access.READ_ONLY)
public abstract ResourceType getResourceType();
}
#Data
#Builder
class SomethingElse extends Resource {
FhirUri someProperty;
CodeableConcept someCode;
List<Reference> someReferences;
#Override
public ResourceType getResourceType() {
return ResourceType.SOMETHING_ELSE;
}
}
And an example instance of the SomethingElse class:
SomethingElse somethingElse = SomethingElse.builder()
.someProperty(FhirUri.from("some-simple-uri"))
.someCode(new CodeableConcept())
.someReference(List.of(new Reference()))
.build();
somethingElse.setId(FhirString.randomUuid());
somethingElse.setExtension(new ArrayList<>());
When I tell any mapper (or, for example, use a Spring service) to map the SomethingElse class into JsonNode, I can, for example, end up with empty objects and arrays, like this:
ObjectMapper mapper = getUntouchableMapper();
JsonNode somethingElseNode = mapper.valueToTree(somethingElse);
System.out.println(somethingElseNode.toString());
Becomes:
{
"resourceType": "SomethingElse",
"id": "00000000-0002-0004-0000-000000000000",
"someProperty": "some-simple-uri",
"someCode": {},
"someReferences": [{}],
"extension": []
}
According to FHIR, this should actually look like:
{
"resourceType": "SomethingElse",
"id": "00000000-0002-0004-0000-000000000000",
"someProperty": "some-simple-uri"
}
To summarize
How do I preserve the serialization mechanisms already in place, regardless of the ObjectMapper used, and somehow remove empty lists and objects from outgoing JSON produced by the Jackson serialization process?
Edit:
I also tried #JsonInclude(JsonInclude.Include.NON_EMPTY), which did omit empty list implementations. However, the vast majority of data in this library is represented by POJOs that serialize to maps and primitives, and this annotation only works if they are represented directly by maps and primitives in the model.
The solution is to use a custom #JsonInclude, which is new in Jackson 2.9. Thank you #dai for pointing me back towards this functionality.
On the base Resource class, this looks like:
#JsonInclude(value = JsonInclude.Include.CUSTOM, valueFilter = FhirJsonValueFilter.class)
class Resource implements FhirTypeInterface {
...
#Override
public boolean isEmpty() {
//Details omitted for simplicity
}
}
For visibility, the interface used above:
interface FhirTypeInterface {
boolean isEmpty();
}
And my custom definition for FhirJsonValueFilter implements all of the functionality of JsonInclude.Include.NON_EMPTY but also adds functionality for checking against a method implemented by FHIR types (implementation of this is not relevant to the answer).
public class FhirJsonValueFilter {
#Override
public boolean equals(Object value) {
return !getWillInclude(value);
}
/**
* Returns true for an object that matched filter criteria (will be
* included) and false for those to omit from the response.
*/
public boolean getWillInclude(Object value) {
//Omit explicit null values
if (null == value) {
return false;
}
//Omit empty collections
if (Collection.class.isAssignableFrom(value.getClass())) {
return !((Collection) value).isEmpty();
}
//Omit empty maps
if (Map.class.isAssignableFrom(value.getClass())) {
return !((Map) value).isEmpty();
}
//Omit empty char sequences (Strings, etc.)
if (CharSequence.class.isAssignableFrom(value.getClass())) {
return ((CharSequence) value).length() > 0;
}
//Omit empty FHIR data represented by an object
if (FhirTypeInterface.class.isAssignableFrom(value.getClass())) {
return !((FhirTypeInterface) value).isEmpty();
}
//If we missed something, default to include it
return true;
}
}
Note that the custom omission filter uses Java's Object.equals functionality, where true means to omit the property, and I've used a second method to reduce confusion in this answer.

How to serialize collection as empty list depending on runtime condition with jackson

We have a business requirement that elements of child collections of entities (we use JPA) in our spring-boot application shouldn't be visible in rest api if the user doesn't have permissions to view child entity.
Right now we use AOP to wrap all get methods in our services so that they do something like this if (!allowed("ChildView")) {entity.setChildren(new ArrayList<>())} which doesn't seems like a good solution to me for a few reasons. First of all relationship between permission name and collections setter is hardcoded outside of entity. Also modifying actual object because we don't want to show something about it in REST api seems kind of strange. You don't remove something if you don't want to show it. You can just hide it. So I thought why not hide it when serializing?
So I can see how to ignore properties completely at runtime via Mixin and #JsonIgnore but I can't find how to return empty list instead.
Ideally I thing of an API like that.
class Entity {
#OneToMany
#AuthSerialize("ChildView", default=Collections.emptyList())
Collection<Child> children;
}
Current solution looks something like this.
Map<Class<? extends BaseEntity>, Map<String, Consumer<BaseEntity>> protectors;
process(BaseEntity e) {
protectors.getOrDefault(e.getClass(), Collectoions.emptyMap())).forEach((permission, clearer) ->
if !allowed(permission) clearer.accept(e)
)
I think the "not wasting cycles" is over-engineering. It might be a valid assertion if you're serializing a million entities per second. Otherwise the JVM will optimize the "hot spot" for you. And anyway, that won't be the bottleneck in your application architecture.
If you know your entities have a "children" array field in common, you might want to apply the same JsonSerializer to all of them, by simply maintining a Map of the compatible classes.
You have to understand that Jackson has its own limitations. If you need something more than that, you might want a totally custom solution. This is the best you can obtain with Jackson.
Hope the answer is satisfactory.
You can use a custom JsonSerializer<T>.
class EntitySerializer extends StdSerializer<Entity> {
private static final long serialVersionUID = 1L;
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
EntitySerializer() {
super(Entity.class);
}
#Override
public void serialize(
final Entity value,
final JsonGenerator generator,
final SerializerProvider provider) throws IOException {
final TreeNode jsonNode = OBJECT_MAPPER.valueToTree(value);
if (!AuthUtils.allowed("ChildView")) {
final TreeNode children = jsonNode.get("children");
if (children.isArray()) {
((ContainerNode<ArrayNode>) children).removeAll();
}
}
generator.writeTree(jsonNode);
}
}
However, as you can see we are using an ObjectMapper instance inside our JsonSerializer (or would you prefer manually "writing" each field with JsonGenerator? I don't think so :P). Since ObjectMapper looks for annotations, to avoid infinite recursion of the serialization process, you have to ditch the class annotation
#JsonSerialize(using = EntitySerializer.class)
And register the custom JsonSerializer manually to the Jackson ObjectMapper.
final SimpleModule module = new SimpleModule();
module.setSerializerModifier(new BeanSerializerModifier() {
#Override
public JsonSerializer<?> modifySerializer(
final SerializationConfig config,
final BeanDescription beanDesc,
final JsonSerializer<?> serializer) {
final Class<?> beanClass = beanDesc.getBeanClass();
return beanClass == Entity.class ? new EntitySerializer() : serializer;
}
});
final ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(module);
Finally, you just have to use the ObjectMapper, or let your framework use it.
As you're using Spring, you can register a #Bean of type ObjectMapper, marked as #Primary, or you can register a #Bean of type Jackson2ObjectMapperBuilder.
Previous answer.
As the allowed method is static, that means it can be accessed from "everywhere".
After fiddling a little bit with Jackson, I'll give you the first of the two options, as I'm still working on the second one.
Annotate your class with
#JsonSerialize(converter = EntityConverter.class)
public class Entity { ... }
Here you're specifying a custom Converter.
The Converter implementation is pretty neat.
Inside the static block I'm simply getting the Auth annotation value, but that is optional, you can do what you feel like is best for your usecase.
class EntityConverter extends StdConverter<Entity, Entity> {
private static final String AUTH_VALUE;
static {
final String value;
try {
final Field children = Entity.class.getDeclaredField("children");
final AuthSerialize auth = children.getAnnotation(AuthSerialize.class);
value = auth != null ? auth.value() : null;
} catch (final NoSuchFieldException e) {
// Provide appropriate Exception, or handle it
throw new RuntimeException(e);
}
AUTH_VALUE = value;
}
#Override
public Entity convert(final Entity value) {
if (AUTH_VALUE != null) {
if (!AuthUtils.allowed(AUTH_VALUE)) {
value.children.clear();
}
}
return value;
}
}
Let me know if this is sufficient, or you'd prefer a more complex solution.
You could use the Mixin to override the getter method:
class noChildViewEntity {
public Collection<Child> getChildren() {
return new ArrayList<>();
}
}

How to use Jackson to deserialize external Lombok builder class

I have a 3rd party Lombok builder POJO, one that I cannot modify, that I want to serialize using jackson. Notably it does not have a NoArgsConstructor.
#Data
#Builder
public class ExternalClass {
private String name;
private String data;
// etc.
}
On the surface this would appear to be simple, but it is incredibly frustrating in practice as each possible option seems to be counteracted by a different complication. In essence, I'm having trouble getting an external Lombok builder to work with a jackson mixin.
Lombok produces fluent setters of the style .name(String name) while Jackson's built-in builder deserializer expects .withName(String name). Lombok documentation, and recipes elsewhere such as here suggest using #JsonDeserialize(builder=ExternalClass.ExternalClassBuilder.class) in conjunction with #JsonPOJOBuilder(withPrefix="") on a predeclared inner stub builder. But this is not possible because the Lombok class is in an external library.
Applying these annotations to a mixin has no effect.
#JsonDeserialize(ExternalClass.ExternalClassBuilder.class)
public abstract class ExternalClassMixin {
#JsonPOJOBuilder(withPrefix="")
public static ExternalClassBuilder {
}
}
The only approach I've found that works is to leverage the package-access AllArgsConstructor created by #Builder and populate the mixin with the following constructor
public abstract class ExternalClassMixin {
#JsonCreator public ExternalClassMixin(
#JsonProperty("name") String name,
#JsonProperty("data") String data,
// etc.
) {}
}
This is obviously not desirable as it requires iterating and hard-coding every class property explicitly, making the mixin fragile to any change in the external POJO.
My question is - is there a robust, maintainable way to serialize this external builder class using Jackson without modifying it, using either a mixin or maybe a full blown deserializer?
Update
I implemented the excellent answer by #jan-rieke, including the suggestion to use reflection to seek out the inner builder class.
...
public Class<?> findPOJOBuilder(AnnotatedClass ac) {
Class<?> innerBuilder;
try {
innerBuilder = Class.forName(ac.getName()+"$"+ac.getRawType().getSimpleName()+"Builder");
log.info("Builder found: {}", ac.getName());
return innerBuilder;
} catch( ClassNotFoundException e ) {
return super.findPOJOBuilder(ac);
}
}
You can customize your ObjectMapper as follows:
ObjectMapper mapper = new ObjectMapper();
mapper.setAnnotationIntrospector(new JacksonAnnotationIntrospector() {
#Override
public Class<?> findPOJOBuilder(AnnotatedClass ac) {
if (ExternalClass.class.equals(ac.getRawType())) {
return ExternalClass.ExternalClassBuilder.class;
}
return super.findPOJOBuilder(ac);
}
#Override
public Value findPOJOBuilderConfig(AnnotatedClass ac) {
if (ac.hasAnnotation(JsonPOJOBuilder.class)) {
return super.findPOJOBuilderConfig(ac);
}
return new JsonPOJOBuilder.Value("build", "");
}
});
This will
explicitly configure that deserialization for ExternalClass uses its builder, and
set the default prefix for builder setter methods to "" (except when the #JsonPOJOBuilder annotation is present).
If you do not want to list all external classes explicitly in findPOJOBuilder(), you can of course programmatically look into the class to check whether it has a inner class that looks like a builder.
This can be accomplished by creating two mixins: one for ExternalClass (specifying the builder to use) and one for ExternalClass.ExternalClassBuilder (specifying the lack of a prefix in the builder methods).
#JsonDeserialize(builder = ExternalClass.ExternalClassBuilder.class)
public interface ExternalClassMixin {
}
#JsonPOJOBuilder(withPrefix="")
public interface ExternalClassBuilderMixin {
}
This serializes and deserializes the JSON in the desired manner:
String json = "{\"name\": \"The Name\", \"data\": \"The Data\"}";
ObjectMapper mapper = new ObjectMapper()
.addMixIn(ExternalClass.class, ExternalClassMixin.class)
.addMixIn(ExternalClass.ExternalClassBuilder.class, ExternalClassBuilderMixin.class);
System.out.println(mapper.readValue(json, ExternalClass.class));
System.out.println(mapper.writeValueAsString(mapper.readValue(json, ExternalClass.class)));
Output:
ExternalClass(name=The Name, data=The Data)
{"name":"The Name","data":"The Data"}

Runtime annotations design and performance

I have a java api which performs an external resource lookup and then maps the values to a Pojo. To do this, the api needs the field names of the Pojo as string values, something like:
public <F> F populatePojoFields(String primaryField, String secondaryField);
This works fine, however passing the pojo field names as String to the api does not feel right. I was able to change this by writing marker annotations for the pojo, so now it is like
public class POJO {
#Primary //custom marker annotation
private int mojo;
#Secondary //custom marker annotation
private String jojo;
}
String primaryField = getFieldNameUsingReflection(Pojo.class, Primary.class)
String secondryField = getFieldNameUsingReflection(Pojo.class, Secondary.class)
Pojo pojo = populatePojoFields(primaryField, secondaryField);
This way I don't have to keep track of string values, I can just add marker annotations to the Pojo fields. This works fine, but I'm worried about performance. Is this a standard way to do things? as keeping hardcoded string values is more efficient than looking up the field names every time we need to call the api. Is there a better way to do this?
If you call getFieldNameUsingReflection often you can think to cache the result of this call.
You can use a singleton class with internal Map with a code like the following:
public class SingletonMapPrimarySecondary {
Map<Class, String> mapPrimary;
Map<Class, String> mapSecondary;
// TODO: Handle mapPrimary and mapSecondary creation and singleton pattern
public String getPrimary(Class clazz) {
String primary = mapPrimary.get(clazz);
if (primary == null) {
primary = getFieldNameUsingReflection(clazz, Primary.class);
mapPrimary.put(clazz, primary);
}
return primary;
}
public String getSecondary(Class clazz) {
// TODO: Similar to getPrimary
}
}

Change field case with an ObjectMapper

I think I need to create a specialist ObjectMapper and cannot find any sample code to start the process.
The creator of the JSON is using .Net and public properties and therefore uses field names with an uppercase initial. I am parsing the JSON into POJOs so I would like to use a lowercase initial.
At their end:
public class Facet
{
public string Name { get; set; }
public string Value { get; set; }
}
At my end I must therefore have:
public class Facet {
public String Name;
public String Value;
}
I would much prefer:
public class Facet {
public String name;
public String value;
}
Am I right that this could be done with an ObjectMapper?
Your first issue can be addressed very simply with the #JsonProperty annotation:
// java-side class
public class Facet
{
#JsonProperty("Name")
public String name;
#JsonProperty("Value")
public String value;
}
Now the ObjectMapper will match up the differently-cased field names. If you don't want to add annotations into your classes, you can create a Mix-in class to stand in for your Facet:
public class FacetMixIn
{
#JsonProperty("Name")
public String name;
#JsonProperty("Value")
public String value;
}
objectMapper.getDeserializationConfig().addMixInAnnotations(Facet.class, FacetMixIn.class);
This will achieve the same thing, without requiring additional annotations in your Facet class.
Instead of annotating each field, the Jackson ObjectMapper can be configured to use a built-in or custom PropertyNamingStrategy, to apply a consistent translation between Java property/field names and JSON element names.
For example:
myObjectMapper.setPropertyNamingStrategy(PascalCaseStrategy);
This problem could be solved from Jackson 2.5.0 like this:
ObjectMapper mapper = new ObjectMapper();
mapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
From the javadoc:
com.fasterxml.jackson.databind.MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES
Feature that will allow for more forgiving deserialization of incoming
JSON. If enabled, the bean properties will be matched using their
lower-case equivalents, meaning that any case-combination (incoming
and matching names are canonicalized by lower-casing) should work.
Note that there is additional performance overhead since incoming
property names need to be lower-cased before comparison, for cases
where there are upper-case letters. Overhead for names that are
already lower-case should be negligible however.
Feature is disabled by default.
Since:
2.5
Just a quick update as I was looking for same answer and a code snippet objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
Since v 2.13 use builder:
XmlMapper xmlMapper = (XmlMapper) getObjectMapper();
private ObjectMapper getObjectMapper() {
return XmlMapper.builder()
.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true)
.build();
}

Categories

Resources