Closed mdzaebel closed 3 years ago
+1, we have this kind of implementation in Apache Johnzon as well - it is portable: https://github.com/apache/johnzon/blob/master/johnzon-json-extras/src/test/java/org/apache/johnzon/jsonb/extras/polymorphism/PolymorphicTest.java#L84
Yes, thats helpfull. Many implementations have a concepts with annotations or other strategies. I'd prefer also one without annotations, as this would enable a transformation without changing legacy classes. Also interesting, why did the expert group omit this feature in 1.0?
@mdzaebel What is likely not possible is to make it transparent without any config (type=fully qualified name) so it requires to have explicit aliases for all children and whitelist the allowed children otherwise you get immediately a security issue. Therefore it is not strictly omitted in 1.0 since it is trivial to handle it in a (de)serializer. Hope it helps.
At first, thanks so much for your help, which is really important for us (big insurance company). E.g. I did not know about the security aspect. I'm not an expert, so could you show me a serializer, that inserts a $type attribute and a deserializer that creates the instances out of $type? Currently, the security aspect could be solved by testing an instanceof relation in our case.
May be JSON Binding 1.0 rather focus on the wrapping strategy, documented in https://javaee.github.io/javaee-spec/javadocs/javax/json/bind/serializer/JsonbDeserializer.html. So the usecase above seems to be difficult, but should be supported in future.
@mdzaebel all the code is contained in this class: https://github.com/apache/johnzon/blob/master/johnzon-json-extras/src/main/java/org/apache/johnzon/jsonb/extras/polymorphism/Polymorphic.java - you can also import johnzon-extras module in your project and not fork it. Then just configure it as mentionned in previous comment (with the example link).
Great stuff. That should be sufficient. Thank you Romain!
With Yasson, your serializer doesn't work (Yasson Issue) e.g.
public void serialize(Dog obj, JsonGenerator generator, SerializationContext ctx) {
ctx.serialize(new Wrapper(obj.getClass().getName(), obj), generator); // shortened
}
leads to "Recursive reference has been found in class ...", but isn't this correct, as the identical object has to be serialized in a deeper level recursively?
Roman Grigoriadi now explained, that Yasson has to be used slighty different (see Issue)
This feature is definitely a must to have so I thought about it and these are my suggestions which should be covered in terms of polymorphic (de)serialization.
Global property Every serialized instance should have one additional property with the class name. This would be disabled by default and allowed to be configured or set by a property.
Annotation based I have not yet come up with specific annotation name but this annotation could be added to the parent type of the classes user wants to (de)serialize. This will add new property to json with the class name of the instance. It should also have possibility to set "alias" name to each sub class and should not require to set exact class names in resulting json.
Polymorphic configuration This would be part of the configuration which would allow user to add the same configuration possibility as in 2. even for classes one doesn't control or if he doesn't want to change existing classes.
It would also be possible to specify the name of the new property in each option mentioned above.
I would also suggest two levels of how strict it should be. For example aliases are set for some set of classes and during serialization is "not mentioned" class found.
What do you guys think about it? Any suggestions?
@Verdent -10000 for your "1", this is exactly the 0-day vulnerability java serialization got and jackson is still trying to fix. 2 requires to use aliases - and it is not an option - known from the server (and not class names) for the same reason. Isn't 3 a custom deserializer - so already built-in?
Yes I know about this. This would be just for those who want to use it internally. And there should be also warning that this might be potential security problem. But I wrote it here because it could be fairly simple to set up for a user.
In case of that 2. I would say that aliases are not that bad way to go. You have only several possible values and those are not invoked on the server. They are matched with corresponding class. What is wrong with that option? :-)
The 3. option was more or less 2. but in configuration way. You can create deserializer, but I was trying to find some way how to do it easier and more "automatic".
My point on 2 was that only aliases are an option (being required and not just one option) since classname is a no go.
@rmannibucau I agree that option 1 adds a vulnerability. But if it's switched off by default and users know that enabling it opens a security hole it's fine. Basic HTTP Authentication is an even bigger security hole, but still used. The same I can say for option 2. It's more secured, but opens a vulnerability in case alias is not specified. Again, users should know about it before enabling it. Do we have other options to make it more secured?
We dont have to provide it at all, basic is still there cause it had been possible if you follow me. Off by default = on in prod (dont ask me how many spring dev actuators are in prod). What is limiting with aliases? Nothing except having the meta model we work in another issue to decorate external classes (a bit like cdi metamodel but lighter) so let's do it safe?
What about defining a list of classes which we allow to serialize/deserialize in configuration. If JSONB tries to deserialize a class which is not in configuration it will throw an exception. It should solve the security issue, is it?
JsonbConfig config = new JsonbConfig()
.serializable("com.test.model").
.serializableAndDeserializable(com.test.SerializableClass);
// Create Jsonb with custom configuration
Jsonb jsonb = JsonbBuilder.create(config);
// Works fine
SerializableClass c = Jsonb.fromJson("some json", SerializableClass.class);
// Throws exception
NotSerializableClass c = Jsonb.fromJson("some json", NotSerializableClass.class);
// Works fine
Jsonb.toJson(com.test.model.Model);
// Throws exception
Jsonb.toJson(com.test.otherpackage.Model);
toJson can pass but the fromJson is the one which must be controlled. Your solution works.
@m0mus serializable(...) is an approach to handle security problems for polymorphic instantiation. It's only one aspect of it and should be defined within a polymorphic configuration. serializable(...) could be missunderstood as a general constraint, rather than a special one for polymorphic instantiation. I would drop duplicate methods like serializableAndDeserializable and use one method in both directions. Differences can always be defined with new JsonbConfig
instances and should not occur often. So I would prefer a whitelisting approach like:
/** Polymorphic [de]serialization */
public JsonbConfig polymorphic(Polymorphic polymorphic);
Where Polymorphic could be defined like e.g.:
class Polymorphic {
String typeAttribute="@type";
Function<Class<?>, String>stringResolver; // String alias of typeAttribute
Function<String, Class<?>>classResolver; // Deserialzation of alias value
Strategy strategy; // How to serialize class types
/** Classes/interfaces of which subclasses/implementations are handled:
* Serialization: typeAttribute added.
* Deserialization: Instantiation by typeAttribute */
Class<?>[] classes;
}
This solves the security issue and the polymorphic configuration.
The proposals of @Verdent have to be decided. I would support them completely. It might be helpful to analyse the concepts already used by other implementations.
jsonio used kinf of that and it failed in some of our project cause the lack of evolutivity in time (too rigid) + payloads where too big. Adapters/deserializers enable to be more clever and bypass type attributes IMHO since they can identify the type per attributes directly and not change the shape of the serialization which breaks js clients for example. So I'd prefer a lighter solution/abstraction if generic and not adapter based and not something against js. So at the end it is just providing a default serializer/adapter impl so let s keep it simple without new concept maybe? The whitelist is still needed though.
Automatic type detection via attributes could also be a candidate for the "strategy". However, in many cases it's not sufficient, as attributes/values might be the same for different instance types. The concept has to be evolutive, thats an important point (what do you mean by "too rigid"?). E.g., if classnames change, it should be possible to use older and newer formats, which would emphasize the necessity of a strong alias concept. For internal communication, payload is not a problem but of course, JS-requirements have to be solvable too. Why does a type attribute break JS clients?
@mdzaebel there is no interoperability standard at all (in the sense "commonly used standard") so if we change the shape of the payload then the clients will not consume it right (missing wrapper, custom attribute). It is likel producing a record following a schema without control. Therefore it must only be activable but never activated without a stringly explicit flag (annotation, no config probably).
@rmannibucau Why no config? This would be more dynamic, while annotations are fixed at classes/methods/parameters. Each strategy has it's own settings, that have to be configurable by preferably one polymorphic configuration.
@rmannibucau Just like Yasson, also Johnzon 1.1.13 fails at the same location:
public void serialize(Dog obj, JsonGenerator generator, SerializationContext ctx) { ctx.serialize(new Wrapper(obj.getClass().getName(), obj), generator); // shortened }
What actually happens is a Stack Overflow due to endless recursion:
Caused by: java.lang.StackOverflowError
at org.apache.johnzon.mapper.MapperConfig.findObjectConverter(MapperConfig.java:186)
at org.apache.johnzon.mapper.MapperConfig.findObjectConverterWriter(MapperConfig.java:175)
at org.apache.johnzon.mapper.MappingGeneratorImpl.doWriteObject(MappingGeneratorImpl.java:153)
at org.apache.johnzon.mapper.MappingGeneratorImpl.writeObject(MappingGeneratorImpl.java:111)
at org.apache.johnzon.jsonb.serializer.JohnzonSerializationContext.serialize(JohnzonSerializationContext.java:41)
at org.apache.johnzon.jsonb.extras.polymorphism.Polymorphic$Serializer.serialize(Polymorphic.java:64)
at org.apache.johnzon.jsonb.JsonbAccessMode$WriterConverters.lambda$new$0(JsonbAccessMode.java:831)
at org.apache.johnzon.mapper.MappingGeneratorImpl.doWriteObjectBody(MappingGeneratorImpl.java:306)
at org.apache.johnzon.mapper.MappingGeneratorImpl.writeValue(MappingGeneratorImpl.java:463)
at org.apache.johnzon.mapper.MappingGeneratorImpl.doWriteObjectBody(MappingGeneratorImpl.java:345)
at org.apache.johnzon.mapper.MappingGeneratorImpl.doWriteObject(MappingGeneratorImpl.java:167)
at org.apache.johnzon.mapper.MappingGeneratorImpl.writeObject(MappingGeneratorImpl.java:111)
Apparently even latest Johnzon tries to wrap again and again and again as soon once it serializes the wrapper itself but then sees the wrapper's
@mkarg it looks expected and normal with current spec - thought throwing a JsonbException("invalid") could have been saner. This code works using annotations and not global converters which leads to that. We likely don't want to skip serializer/deserializer/adapter in SerializationContext. If you set your impl globally (on jsonbconfig), it must likely have the standard threadlocal to prevent the loop if possible. That said, this use case does not fit serializer API which is really about how to serializer, it fits adapter API which is about changing the shape of a model then the new shape can have a serializer if you want I think.
@rmannibucau I do not understand what you try to say, really! If I do not bind by annotations but by config, then I get the error message that only annotation binding is supported by Johnzon. So how to make this work actually? It is the original Johnzon Extras, it is the latest released Jonhzon, and it is exctly the example from the serializer. So Johnzon is actually not able to execute its own Extras?!
@mkarg: wait, think topics are getting interlaced here so let me try to list the different points:
@rmannibucau Thank you for the clarification.
BTW, how shall a serializer deal with that at all (it cannot guess which other serializers / deserializers have to be in place still, so all it could do is use Jsonb default config, which would be wrong is many cases)?
@mkarg 1. usage is explicited here http://johnzon.apache.org/ (implicitly other usages are not intended but feel free to PR to clarify it), 2. no because of 1 ;).
Why don't you use an adapter?
@rmannibucau
As I strongly disagree that not showing a JsonConfig based use case implies other usages are not intended because reading the JSON-B spec mentiones no such limitations in any way, I will file a PR. Stay tuned.
I do not understand "2. no because of 1" as the loop happens with the annotations-secenario (I do not use the JsonbConfig scenario). So why is the endless loop not a bug? I follow exactly the scenario described, using no explicit config at all but just the annotations!
I do not use an adapter because my current job is "replace our custom code by Johnzon Extras Polymorphic" and that means, no adapters but that serializer. My issue is not "how to make some polymorphism work" (I already have that) but "how to make Johnzon Extras Polymorphic not run into an endless loop while using no JsonbConfig but just annotations)!
@mkarg this should likely move to johnzon user list but long story short the intended usage is to rely on annotations so if you don't then any unexpected behavior is actually not a bug. Without jsonbconfig global registration the polymorphic usage works - if not it is a bug we will fix but it is harnessed so should be very specific.
@rmannibucau Agreed to switch over to Johnzon, as not general a JSON-B topic apparently. As I am relying on annotations and as I am not using JsonbConfig I will file an issue with Apache's JIRA. Thanks for all! :-) - See JOHNZON-273.
@rmannibucau FYI
- I will file a PR. Stay tuned. See https://github.com/apache/johnzon/pull/43. :-)
A difficult example of polymorphic [de]serialisation would be Throwable
, as it is cyclic in getCause
() by default, is a built-in class (so can't be annotated) and has a getter (e.g. getMessage
()), that has different private field-name detailMessage
. Furthermore, it has getStacktrace
() with StackTraceElement
[]'s, that have no noArgConstructor and a again Java built-in classes. Additionally, subclasses could have various custom payload fields.
Cyclic serialization is handled separately - we have a flag for that in johnzon for instance. But it is transversal to polymorphism so we should keep it unrelated and orthogonal IMHO.
Yes, of course, cyclic serialisation is a different issue. I just wanted to give a testcase that has the hardest problems in it. So, if we could de/serialize Exceptions polymorphically easily, this would be a big/important step for the next JSON-BIND version.
@mdzaebel we solve recursive references via writing a JsonPointer instead of the original value. When reading back the JSON we use the same Instance to which the JsonPointer pointer points to One of the restrictions right now is that you must use the same ordering strategy for writing and reading right now.
Thanks for the tip.
@mdzaebel My proposal about defining a scope for serialization and deserialization is wider than the polymorphism case. @Verdent proposal is fine, but we should allow using it only with scope definition. In this case we avoid security problems.
To try and summarize here, at the core we need to have the following items:
As for what this code might look like, I think the proposal by @mdzaebel looks promising. I like the idea of offloading the detailed config to a separate class like this:
public JsonbConfig polymorphic(Polymorphic polymorphic);
If we keep adding more methods to JsonbConfig
it will quickly become overwhelming for users because of too many methods.
Suppose I have the following classes:
A user could configure polymorphism like this:
Polymorphic poly = Polymorphic.create()
.withTypeAttribute("__type") // optional, defaults to something sensible
.withClasses(Dog.class, Cat.class)
.build();
Jsonb jsonb = JsonbBuilder.create(new JsonbConfig()
.withPolymorphic(poly));
Dog dog = // ...
jsonb.toJson(dog);
The resulting JSON would be:
{
"__type": "com.foo.Dog",
"dogName": "Spot"
}
Then, it could easily be deserialized by another Java client using JSON-B.
However, not every service producing JSON will be Java obviously, and we shouldn't expect clients to know internal details about the Java application parsing the data. Namely, a JavaScript frontend shouldn't need to know that a Java backend has a class called com.foo.Dog
, or even that it decided to use polymorphism to represent the JSON data.
To give a concrete example, suppose a JavaScript frontend has some other way of representing type information, perhaps:
{
"animalType": "1", // suppose '1' means "Dog" for the frontend
"dogName": "Spot",
etc...
}
This is where the the proposed stringResolver
and classResolver
variables could come in handy:
Polymorphic poly = Polymorphic.create()
.withClassResolver((animalType) -> {
if ("1".equals(animalType)) return com.foo.Dog.class;
if ("2".equals(animalType)) return com.foo.Cat.class;
else // throw exception
})
// ...
Regarding @Verdent 's proposal, specifically item (2), I don't think an annotative approach is appropriate because in order to be secure, interfaces would have to declare implementations. (If impls declared their interfaces it could be a security issue) since scope could not be limited.
I don't like the way Yasson implemented this, which looks like this:
public class MyClass {
@ImplementationClass(Dog.class)
private Animal animal;
}
since the entire point of using an interface is defeated by needing to annotate the field/method with the implementation class.
I'd keep JsonbConfig the entry point, rational being to keep a single entry point for end user which is generally appreciated. No issue for JsonbConfig to create a PolymorphicBuilder (likely just an interface and no implementation provided to let it be provider dependant as usual).
For consistency it should be a JsonbConfig#withPolymorphicConfig (and probably PolymorphicConfig[Builder] for the config instance) IMHO.
Finally the @ImplementationClass is not that nice except on List
Hope it makes sense
yep, we are in agreement
I was asked by @m0mus to make some changes in my proposal and also make it with examples. This is a bit longer, but I think that it illustrates requested functionality pretty well.
Create easy way to serialize and deserialize polymorphic java objects.
Yasson is framework for serialization and deserialization of java objects to json and back, but it still doesn't have automatic way to do it with polymorphic types. Currently when you want to (de)serialize this kind of classes, you need to create your own serializer/deserializer implementation to make it work.
This support could be allowed over
Every serialized instance should have one additional property with the class name. This would be disabled by default and allowed to be configured or set by a property.
When this feature is enabled it needs to have whitelist set up. Whitelist is set by configuration and allows user to define which classes can be deserialized by Yasson. This allows you to avoid potential security risk. Whitelist can be disabled, but it is recommended not to do that. If whitelist is disabled then warning should be printed out to the user.
Class name will be always serialized as the first property in each part of the json.
It is also possible to not set propertyName. If done so, it will be se serialized as json value without name.
Global polymorphism can be enabled directly via configuration properties
GlobalPolymorphicHandling gph = PolymorphicHandling.globalPolymorphicHandler()
.propertyName("someName")
.disableWhitelist()
.create();
WhitelistConfig whitelist = WhitelistConfig.builder()
.packageName("some.package")
.packageName("second.package")
.type(SomeClass.class)
.type(SecondClass.class)
.build();
jsonbConfig.withGlobalPolymophism(gph);
jsonbConfig.withWhitelist(whitelist);
Model
public class Car {
public String name;
}
public class PersonalVehicle extends Car {
public int maxSpeed;
}
public class Truck extends Car {
public int size;
}
public class CarRental {
public ArrayList<Car> carsForRent = new ArrayList<>();
}
Usage
GlobalPolymorphicHandling gph = PolymorphicHandling.globalPolymorphicHandler()
.propertyName("@type")
.create();
WhitelistConfig whitelist = WhitelistConfig.builder()
.packageName("some.package")
.type(ArrayList.class)
.build();
JsonbConfig jsonbConfig = new JsonbConfig()
.withGlobalPolymophism(gph)
.withWhitelist(whitelist);
Jsonb jsonb = JsonbBuilder.create(jsonbConfig).build();
PersonalVehicle personal = new PersonalVehicle();
personal.name = "BMW";
personal.maxSpeed = 250;
Truck truck = new Truck();
truck.name = "MAN";
truck.size = 30;
CarRental carRental = new CarRental();
carRental.carsForRent.add(personal);
carRental.carsForRent.add(truck);
jsonb.toJson(carRental);
Output
{
"carsForRent": {
"@type":"java.util.ArrayList",
"entries":[
{
"@type":"some.package.PersonalVehicle",
"name":"BMW",
"maxSpeed":250
},
{
"@type":"some.package.Truck",
"name":"MAN",
"size":30
}
]
}
}
Output without propertyName set
{
"carsForRent": [
"java.util.ArrayList",
[
[
"some.package.PersonalVehicle",
{
"name":"BMW",
"maxSpeed":250
}
],
[ "some.package.Truck",
{
"name":"MAN",
"size":30
}
]
]
]
}
It is possible to enable polymorphic handling just for specific set of classes. This is enabled
by adding @JsonbPolymorphicType
annotation to the common parent of all classes you want to turn
this on.
Another required annotation here is @JsonbSubType
which allow us to define all possible
child classes of this class and corresponding String
alias to each of them. Note that alias names
can't repeat. Each alias is defined in separate @JsonbSubType annotation. If non existing alias is
found during deserialization an exception should be thrown.
It is also possible to set a little less secure setting here. If you want to (de)serialize also
classes not mentioned in @JsonbSubType
, you have to set className
parameter at
@JsonbPolymorphicType
to true. This behavior is set by default to false and if enabled it
should print warning to the console and also requires to have whitelist set up if not disabled.
@JsonbPolymorphicType
or
@JsonbPolymorphicType(property="@test", className = true)
@JsonbSubType(alias = "test", type = SomeClass.class)
@JsonbSubType(alias = "test2", type = SomeClassTwo.class)
Model
@JsonbPolymorphicType(prefix="@annotType", className = true)
@JsonbSubType(alias = "personal", type = PersonalVehicle.class)
@JsonbSubType(alias = "truck", type = Truck.class)
public class Car {
public String name;
}
public class PersonalVehicle extends Car {
public int maxSpeed;
}
public class Truck extends Car {
public int size;
}
public class MonsterTruck extends Car {
public int wheelSize;
}
public class CarRental {
public ArrayList<Car> carsForRent = new ArrayList<>();
}
Usage
WhitelistConfig whitelist = WhitelistConfig.builder()
.packageName("some.package")
.build();
JsonbConfig jsonbConfig = new JsonbConfig().withWhitelist(whitelist);
Jsonb jsonb = JsonbBuilder.create(jsonbConfig).build();
PersonalVehicle personal = new PersonalVehicle();
personal.name = "BMW";
personal.maxSpeed = 250;
Truck truck = new Truck();
truck.name = "MAN";
truck.size = 30;
MonsterTruck monsterTruck = new MonsterTruck();
truck.name = "MonsterTruck";
truck.wheelSize = 3;
CarRental carRental = new CarRental();
carRental.carsForRent.add(personal);
carRental.carsForRent.add(truck);
carRental.carsForRent.add(monsterTruck);
jsonb.toJson(carRental);
Output
{
"carsForRent": [
{
"@annotType":"personal",
"name":"BMW",
"maxSpeed":250
},
{
"@annotType":"truck",
"name":"MAN",
"size":30
},
{
"@annotType":"some.package.MonsterTruck",
"name":"MonsterTruck",
"wheelSize":3
}
]
}
When we don't have control over some classes or we just don't want to make any changes to existing
code, it is possible to add PolymorphicClassConfig
to JsonbConfig
. It has the same
functionality as in case of annotation handling. It is also possible to handle classes which are not
present as aliases but it requires to have it enabled by method enableClassNames
. If enableClassNames
is enabled, then it is required to have whitelist set up if not disabled.
PolymorphicClassConfig polymorphicConfig = PolymorphicHandling.classHandler(SomeClass.class)
.propertyName("someName")
.enableClassNames()
.disableWhitelist()
.addAlias("alias", Chield.class)
.create();
WhitelistConfig whitelist = WhitelistConfig.builder()
.packageName("some.package")
.build();
jsonbConfig.addPolymorphicClassHandling(polymorphicConfig);
jsonbConfig.withWhitelist(whitelist);
Model
public class Car {
public String name;
}
public class PersonalVehicle extends Car {
public int maxSpeed;
}
public class Truck extends Car {
public int size;
}
public class MonsterTruck extends Car {
public int wheelSize;
}
public class CarRental {
public ArrayList<Car> carsForRent = new ArrayList<>();
}
Usage
JsonbConfig jsonbConfig = new JsonbConfig();
PolymorphicClassConfig polymorphicConfig = PolymorphicHandling.classHandler(Car.class)
.propertyName("@type")
.enableClassNames()
.addAlias("personal", PersonalVehicle.class)
.addAlias("truck", Truck.class)
.create();
WhitelistConfig whitelist = WhitelistConfig.builder()
.packageName("some.package")
.build();
jsonbConfig.addPolymorphicClassHandling(polymorphicConfig);
jsonbConfig.withWhitelist(whitelist);
Jsonb jsonb = JsonbBuilder.create(jsonbConfig);
PersonalVehicle personal = new PersonalVehicle();
personal.name = "BMW";
personal.maxSpeed = 250;
Truck truck = new Truck();
truck.name = "MAN";
truck.size = 30;
MonsterTruck monsterTruck = new MonsterTruck();
truck.name = "MonsterTruck";
truck.wheelSize = 3;
CarRental carRental = new CarRental();
carRental.carsForRent.add(personal);
carRental.carsForRent.add(truck);
carRental.carsForRent.add(monsterTruck);
jsonb.toJson(carRental);
Output
{
"carsForRent": [
{
"@type":"personal",
"name":"BMW",
"maxSpeed":250
},
{
"@type":"truck",
"name":"MAN",
"size":30
},
{
"@type":"some.package.MonsterTruck",
"name":"MonsterTruck",
"wheelSize":3
}
]
}
@Verdent Thanks for this elaborated proposal. I see following points:
May be we should start without annotation overriding as a configuration would enable everything we need?
There is a special Collection case for java.util.Map. Maps because of type erasure at runtime. The Map keys/values should be [de]serialized polymorphically too. E.g. Jackson has problems with it and needs to be called with something like mapper.writerFor(new TypeReference<Map<Key, Value>>(){}).writeValueAsString(map))
where you need to know the types in advance. We should have an idea of how to cope with polymorphics map values.
We use Type at jsonb to cope with it so no rela issue IMHO.
@mdzaebel I definitely want to include also suggestions from @aguibert and @rmannibucau . I was working on this proposal with examples for a few days but I had it already ready when @aguibert post his proposal :-)
We should have a common concept of how to overwrite annotations programmatically ...
I do agree.
With your current proposal, it wouldn't e.g. be possible to make the handling compatible with Jackson, as Collection-Serialisation ...
I do not agree here. I think it would be possible. You would have to specify in config that polymorphic handling is only required for Set.class or Collection.class for example. In case of Object I think that it is exactly covered in one of my examples. Please point me to correct direction if that is not true :-)
The words "Handler" or "Handling" could be deleted. We should reduce the length of names if possible. ...
I do agree. That is fair point.
it could be advantageous to start with a simple predicate, that returns, whether a class should be polymorphic or not ...
I like this idea.
@Verdent Thanks for your reply!
I do not agree here. I think it would be possible. You would have to specify in config that polymorphic handling is only required for Set.class or Collection.class for example. In case of Object I think that it is exactly covered in one of my examples. Please point me to correct direction if that is not true :-)
According to your proposal there are roughly two kinds of serialisation, with additional aliasing:
Whitelisting and aliasing is also possible. Jackson serializes by default arrays in the above Array-Type and objects in the Object-Type, where your approach seems to serialize only as either Object-Type or Array-Type (but not both), But may be, I missunderstood possible configurations? You said that one could configure Set/Collection as polymorphic, but this would rule out the necessary Objects? Hope I'm wrong?
Also, it's not clear to me, what results, if you deserialize a Map<?, Subtype> to Map<?,Object>. Will this result in a "Subtype" created (which we'd need)?
So if you agree to the other points, I'm curious about your revised proposal.
Jackson deserializes objects according to their target type only. However, in secure environments, it could be advantageous to have an option, that simply creates the classes, that are given as the first type attribute. (jackson-user). This would enable the polymorphic transfer of highly generic data types, without the need to think about deserialization at all.
So what is the concrete impact to the previous proposal? Support to parse generic types or change the @type enrichment from string to object?
Object already has rules in the spec and tcks (like numbers ends up as bigdecimal for example) so guess proposal is feature complete now, just need API review?
I'd like to repeat the feature proposal from https://github.com/eclipse-ee4j/yasson/issues/133 here. There should at least be a description of how to implement Serializer/Deserializer that maintain runtime types. As there are more possible strategies, they should be defined too.