Some interfaces, like ResolvableSerializer & ContextualSerializer, modify how Jackson handles a JsonSerializer.
Are there any other such interfaces?
Does Jackson ever modify its behavior for subclasses of JsonSerializer, like StdSerializer, BeanSerializerBase, or BeanSerializer? (other than via standard polymorphism, of course; I'm talking about things like instanceof, or Class.isAssignableFrom(), etc.)
In an implementation of modifySerializer in a subclass of BeanSerializerModifier that I wrote, I return a subclass of JsonSerializer that wraps the argument JsonSerializer. This wrapper overrides only two methods:
serialize: only if certain conditions are met does it call serialize on the wrapped serializer
getDelegatee: returns the wrapped serializer
Questions:
should my wrapper extend some subclass of JsonSerializer instead of just JsonSerializer?
if so, should the subclass depend on the class of the wrapped serializer?
should my wrapper overload any other methods?
should my wrapper implement the same serializer modifier interfaces as the wrapped serializer? If so, then there are two problems:
I have to know every modifier interface, and update my BeanSerializerModifier to handle any new ones that are added to Jackson
I need to have a different wrapper class for each combination of modifier interfaces, which is very cumbersome
Good questions. Here are some thoughts:
Usually you should extend StdSerializer instead of "raw" JsonSerializer.
If serialization is as JSON Scalar value, you may want to extend StdScalarSerializer
Base type does not need to match, in general, although if delegating to Collection or Map serializers you may want to do that -- however, in general, you should need matching. It would get impractical soon as you correctly note.
On overloading: there are a few methods you may choose to overload, and usually just delegate to delegatee:
For polymorphic handling, define "serializeWithType(...)"
isEmpty(), if there are non-null values that correlate with concept of "empty": for example, String "" is considered empty.
acceptJsonFormatVisitor() is necessary to support JSON Schema generation and other type introspection (like generating Avro, CSV and Protobuf schemas, using matching data format modules)
Related
According to the official doc, a Kotlin data class has the following generated functionalities:
equals()/hashCode() pair
toString()
componentN() functions corresponding to the properties in their order of declaration
copy()
Now I understand that these functions are useful sometimes, I feel that in a lot of cases they are not necessary becase regular Kotlin classes already have auto-generated setters and getters. For example, in a Spring controller, if I want to create a DTO class to store and validate the request body, a regular Kotlin class (with validation annotations) seems enough.
I like that data class conveys the semantics that this class is a dumb data container, but I'm concerned that overusing data classes when we don't need their genereted goodies can negatively impact the code size & thus speed. Is there anything (such as an official style guide) that touches on this?
I'm trying to do some polymorphic deseralization of JSON using Jackson, however the list of subclasses is unknown at compile time, so I can't use a #JsonSubtype annotation on the base class.
Instead I want to use a TypeIdResolver class to perform the conversion to and from a property value.
The list of possible subclasses I might encounter will be dynamic, but they are all registered at run time with a registry. So I would appear to need my TypeIdResolver object to have a reference to that registry class. It has to operate in what is essentially a dependency injection environment (i.e I can't have a singleton class that the TypeIdResolver consults), so I think I need to inject the registry class into the TypeIdResolver. The kind of code I think I want write is:
ObjectMapper mapper = new ObjectMapper();
mapper.something(new MyTypeIdResolver(subclassRegistry));
mapper.readValue(...)
However, I can't find a way of doing the bit in the middle. The only methods I can find use java annotations to specify what the TypeIdResolver is going to be.
This question Is there a way to specify #JsonTypeIdResolver on mapper config instead of annotation? is the same, though the motivation is different, and the answer is to use an annotation mixin, which won't work here.
SimpleModule has method registerSubtypes(), with which you can register subtypes. If only passing Classes, simple class name is used as type id, but you can also pass NamedType to define type id to use for sub-class.
So, if you do know full set, just build SimpleModule, register that to mapper.
Otherwise if this does not work you may need to resort to just sharing data via static singleton instance (if applicable), or even ThreadLocal.
Note that in the end what I did was abandon Jackson and write my own much simpler framework based on javax.json that just did the kinds of serialisation I wanted in a much more straightforward fashion. I was only dealing with simple DTO (data transfer object) classes, so it was just much simpler to write my own simple framework.
Is there an Interface that I can extend or some other way to create an Interface whereby the implementing class must be a data class? It would be useful to have access to the data class API methods such as copy().
No, copy method have unique number of parameters for every data class, so it's useless to have such interface. If all your data classes have same field - just create and implement common interface.
So I'm going to preface my answer by saying I don't have experience with Kotlin, but I have plenty of Java experience which as I understand it is similar, so unless Kotlin has a feature that helps do what you want that Java doesn't, my answer might still apply.
If I understand correctly, basically what you're trying to do is enforce that whatever class implements your interface X, must also be a subtype of Y.
My first question would be Why would you want to do this? Enforcing that X only be implemented by subtypes of Y is mixing interface and implementation, which the exact opposite of what interfaces are for.
To even enforce this, you would have to have X extend the interface of Y, either implicitly or explicitly. Since in Java (and presumably Kotlin), interfaces cannot extend objects, you have two options:
1) extend the INTERFACE of data, if it exists (which I don't think it does given what I've been reading about data classes. It sounds more like a baked in language feature than just a helpful code object)
2) Add to your interface the exact method signatures of the methods you want out of data classes. BY doing this, you've gained two things: First, you get your convenience methods whenever a data class implements your interface, and second, you still have the flexibility that interfaces are meant to provide, because now even non-data classes can implement your interface if you need them to, they just have to be sure to define the data classes interface methods manually.
Can someone point me to a good explanation of the criteria a class must meet to use the default deserializaton for Jackson?
I can do a post and supply a HashMap collection of string/jsonObjects, but it won't work with a dataset that contains the same information in a row. I tried implementing my own Deserializer but the response is always "Unprocessable Entity". I have been successful using specific classes, but I am trying to generalize my solution by passing a dataset.
Obviously I need a better understanding of what is happening! TIA!
It depends on rough type of your class. Jackson has specialized handling for:
Arrays of types
Collection implementations
Map implementations
Enums
Other
If type is none of first 4 categories, it will be considered "Other", and expected to follow Java Bean convention of either public fields, or getters and/or setters.
There also has to be a no-argument constructor (need not be public), or another constructor annotated with #JsonCreator -- expect that some public single-argument constructors (String, int/Integer, long/Long, boolean/Boolean) are also accepted when binding from JSON Scalar values.
But to get more information you really should share the actual exception you get: above is just the general idea of what is needed. Jackson can work with all kinds of classes, and is not particularly strict in how classes are defined. But it does have expectations on how various JSON Structures match with POJOs.
So my question is, why to use interfaces or abstract classes? Why are they useful, and for what?
Where can i use them intelligently?
Interfaces allow you to express what a type does without worrying about how it is done. Implementation can be changed at will without impacting clients.
Abstract classes are like interfaces except they allow you to provide sensible default behavior for methods where it exists.
Use and examples depend on the language. If you know Java, you can find examples of both interfaces and abstract classes throughout the API. The java.util collections have plenty of both.
They're useful when you want to specify a set of common methods and properties that all classes that implement/inherit from them have, exposed behaviors that all should provide.
Particularly about interfaces, a class can implement multiple interfaces, so this comes in handy when you're trying to model the fact that its instances must exhibit multiple types of behavior.
Also, as Wikipedia puts it, an interface is a type definition: anywhere an object can be passed as parameter in a function or method call, the type of the object to be exchanged can be defined in terms of an interface instead of a specific class, this allowing later to use the same function exchanging different object types: hence such code turns out to be more generic and reusable.