I am progressing with implementation of major features for Jackson 2.9: https://github.com/FasterXML/jackson/wiki/Jackson-Release-2.9
and finally got third one (out of tentative 11) implemented: https://github.com/FasterXML/jackson-databind/issues/1399 What this allows is specifying that a property should be merged: meaning that instead of always constructing a new value, assigning values to it, recursively, an attempt is made to first check current value (if there is a getter, or field to use for access), and if so, update that value instead. This has been on top-10 requested features list for years. Feature is intended to replace general "deep merge" functionality, as it both allows more granularity (not everything needs to be merged) and turns out to be easier to implement. It uses already existing secondary `deserialize()` method, formerly only used via `mapper.updatingReader(existingValue)` for "shallow" merge. Jackson databind supports this for POJOs, Collections and Maps; for other types, more work would be needed, esp. for 3rd party datatypes. Current (*) API design allows for 3 levels of specifying whether merging should be attempted: 1. Property annotation: static class Config { @JsonSetter(merge=OptBoolean.TRUE) public ServerSettings server = new ServerSettings(124, false); } which applies just for that particular property 2. Type defaulting ObjectMapper mapper = new ObjectMapper(); mapper.configOverride(ServerSettings.class).setSetterInfo( JsonSetter.Value.forMerging()); which is used as the default (in absence of per-property definition) for all properties with matching declared type (here all instances of `ServerSettings`) 3. Global baseline mapper.setDefaultSetterInfo(JsonSetter.Value.forMerging()); which is used if neither per-property nor per-type settings are used. This may be useful for some configuration use cases. ----- Ok, so far so good. This works fine for regular properties. But there are a few issues that may come up, and I have been trying to figure out proper way to deal with. In most cases the general issue is that certain values are simple not mergeable at all, and the question is whether this should result in: (a) Default, non-merging functionality to be used, or (b) Signaling an error to user via InvalidDefinitionException specific cases I can think of include: 1. Property has `null` value 2. No accessor (getter/field) 3. Property is passed as Creator property 4. Property value is immutable and either can not be merged at all (like Integer), or would require elaborate rules (array values) 5. Non-core types via modules may not (yet) support updating 6. Property value requires polymorphic handling, and incoming type may differ from existing value, possibly not mergeableOf these, null handling probably should just proceed with regular handling and not signal error (although it'd be easy enough to add a `MapperFeature` to change this globally). Of these, the last one seems trickiest. But let's go over these in order. Not having accessor would seem like something to ignore and proceed with default handling, although as with `null`s maybe someone somewhere might want signaling. Creator property may be something that can be supported, I'll have to think about that. Not-mergeable thing is something for which I added `MapperFeature.IGNORE_MERGE_FOR_UNMERGEABLE` for, defalting to `true` so that regular deserialization proceeds, As to non-core types... well, support would need to be added. My main concern is just that ideally there would be a way to signal cases that ought to allow merge from others that do not, but this is somewhat difficult to implement. One possibility would be to actually make `IGNORE_MERGE_FOR_UNMERGEABLE` default to false, and rely on `StdScalarDeserializer` and `StringDeserializer` to avoid exception (as primitives/wrappers, Strings, are immutable), but let other types throw exception by default I think all of above can be handled in a way that should work well enough. But the really tricky case might be the polymorphic one. Challenge here is two-fold: 1. Handling of polymorphic wrapper is tricky, as there is not "update existing value" variant for `deserializeWithType()` -- one would probably need to be added 2. If handling is added, what should be done in likely case of existing type not being assign-compatible with type from JSON? Perhaps assignment-compatibility would be required, such that existing value to update must be same type or super-type, and if not, exception thrown; either automatically, or wait for assignment incompatibility? So it perhaps should be possible to support this use case, with limitations that types (default to update, indicted type from data) must be compatible enough for operation to make sense. Anyway -- I would really appreciate suggestions, feedback here. -+ Tatu +- ---- Aside from how to handle problem cases, there's the question of how to configure said handling. There are at least 2 approaches: 1. Addition of MapperFeatures (since these are not configurable on per-call basis), and possible matching JsonFormat.FormatFeature for per-property annotation 2. Changing `@JsonSetter.merge` to be dedicated Enum with more options than just true/false/default. I don't have preference over choices, but going with (2) would be much more difficult to do after 2.9 is released, whereas (1) can be added at any point. -+ Tatu +- (*) current meaning that it is subject to changes for now, esp. based on feedback, improvement ideas. -- You received this message because you are subscribed to the Google Groups "jackson-dev" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. For more options, visit https://groups.google.com/d/optout.
