Skip to content

Commit a2d3a5a

Browse files
committed
Initial version of merging-property-deser (#1399)
1 parent 17ca894 commit a2d3a5a

File tree

9 files changed

+169
-134
lines changed

9 files changed

+169
-134
lines changed

src/main/java/com/fasterxml/jackson/databind/DeserializationConfig.java

-35
Original file line numberDiff line numberDiff line change
@@ -745,15 +745,6 @@ public AnnotationIntrospector getAnnotationIntrospector()
745745
return NopAnnotationIntrospector.instance;
746746
}
747747

748-
/**
749-
* Accessor for getting bean description that only contains class
750-
* annotations: useful if no getter/setter/creator information is needed.
751-
*/
752-
@Override
753-
public BeanDescription introspectClassAnnotations(JavaType type) {
754-
return getClassIntrospector().forClassAnnotations(this, type, this);
755-
}
756-
757748
/**
758749
* Accessor for getting bean description that only contains immediate class
759750
* annotations: ones from the class, and its direct mix-in, if any, but
@@ -775,32 +766,6 @@ public JsonInclude.Value getDefaultPropertyInclusion() {
775766
return EMPTY_INCLUDE;
776767
}
777768

778-
@Override
779-
public JsonInclude.Value getDefaultPropertyInclusion(Class<?> baseType) {
780-
ConfigOverride overrides = findConfigOverride(baseType);
781-
if (overrides != null) {
782-
JsonInclude.Value v = overrides.getInclude();
783-
if (v != null) {
784-
return v;
785-
}
786-
}
787-
return EMPTY_INCLUDE;
788-
}
789-
790-
@Override
791-
public JsonInclude.Value getDefaultPropertyInclusion(Class<?> baseType,
792-
JsonInclude.Value defaultIncl)
793-
{
794-
ConfigOverride overrides = findConfigOverride(baseType);
795-
if (overrides != null) {
796-
JsonInclude.Value v = overrides.getInclude();
797-
if (v != null) {
798-
return v;
799-
}
800-
}
801-
return defaultIncl;
802-
}
803-
804769
/*
805770
/**********************************************************
806771
/* MapperConfig implementation/overrides: other

src/main/java/com/fasterxml/jackson/databind/SerializationConfig.java

-35
Original file line numberDiff line numberDiff line change
@@ -847,15 +847,6 @@ public AnnotationIntrospector getAnnotationIntrospector()
847847
return AnnotationIntrospector.nopInstance();
848848
}
849849

850-
/**
851-
* Accessor for getting bean description that only contains class
852-
* annotations: useful if no getter/setter/creator information is needed.
853-
*/
854-
@Override
855-
public BeanDescription introspectClassAnnotations(JavaType type) {
856-
return getClassIntrospector().forClassAnnotations(this, type, this);
857-
}
858-
859850
/**
860851
* Accessor for getting bean description that only contains immediate class
861852
* annotations: ones from the class, and its direct mix-in, if any, but
@@ -887,32 +878,6 @@ public JsonInclude.Value getDefaultPropertyInclusion() {
887878
return _serializationInclusion;
888879
}
889880

890-
@Override
891-
public JsonInclude.Value getDefaultPropertyInclusion(Class<?> baseType) {
892-
ConfigOverride overrides = findConfigOverride(baseType);
893-
if (overrides != null) {
894-
JsonInclude.Value v = overrides.getInclude();
895-
if (v != null) {
896-
return v;
897-
}
898-
}
899-
return _serializationInclusion;
900-
}
901-
902-
@Override
903-
public JsonInclude.Value getDefaultPropertyInclusion(Class<?> baseType,
904-
JsonInclude.Value defaultIncl)
905-
{
906-
ConfigOverride overrides = findConfigOverride(baseType);
907-
if (overrides != null) {
908-
JsonInclude.Value v = overrides.getInclude();
909-
if (v != null) {
910-
return v;
911-
}
912-
}
913-
return defaultIncl;
914-
}
915-
916881
/*
917882
/**********************************************************
918883
/* Configuration: other

src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfig.java

+26-5
Original file line numberDiff line numberDiff line change
@@ -319,12 +319,14 @@ public JavaType constructSpecializedType(JavaType baseType, Class<?> subclass) {
319319
public BeanDescription introspectClassAnnotations(Class<?> cls) {
320320
return introspectClassAnnotations(constructType(cls));
321321
}
322-
322+
323323
/**
324324
* Accessor for getting bean description that only contains class
325325
* annotations: useful if no getter/setter/creator information is needed.
326326
*/
327-
public abstract BeanDescription introspectClassAnnotations(JavaType type);
327+
public final BeanDescription introspectClassAnnotations(JavaType type) {
328+
return getClassIntrospector().forClassAnnotations(this, type, this);
329+
}
328330

329331
/**
330332
* Accessor for getting bean description that only contains immediate class
@@ -363,7 +365,16 @@ public BeanDescription introspectDirectClassAnnotations(Class<?> cls) {
363365
*
364366
* @since 2.7
365367
*/
366-
public abstract JsonInclude.Value getDefaultPropertyInclusion(Class<?> baseType);
368+
public JsonInclude.Value getDefaultPropertyInclusion(Class<?> baseType) {
369+
ConfigOverride overrides = findConfigOverride(baseType);
370+
if (overrides != null) {
371+
JsonInclude.Value v = overrides.getInclude();
372+
if (v != null) {
373+
return v;
374+
}
375+
}
376+
return getDefaultPropertyInclusion();
377+
}
367378

368379
/**
369380
* Accessor for default property inclusion to use for serialization,
@@ -374,8 +385,18 @@ public BeanDescription introspectDirectClassAnnotations(Class<?> cls) {
374385
*
375386
* @since 2.8.2
376387
*/
377-
public abstract JsonInclude.Value getDefaultPropertyInclusion(Class<?> baseType,
378-
JsonInclude.Value defaultIncl);
388+
public JsonInclude.Value getDefaultPropertyInclusion(Class<?> baseType,
389+
JsonInclude.Value defaultIncl)
390+
{
391+
ConfigOverride overrides = findConfigOverride(baseType);
392+
if (overrides != null) {
393+
JsonInclude.Value v = overrides.getInclude();
394+
if (v != null) {
395+
return v;
396+
}
397+
}
398+
return defaultIncl;
399+
}
379400

380401
/**
381402
* Accessor for default format settings to use for serialization (and, to a degree

src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java

+34-2
Original file line numberDiff line numberDiff line change
@@ -509,11 +509,27 @@ protected void addBeanProps(DeserializationContext ctxt,
509509
* other types, and only then create constructor parameter, if any.
510510
*/
511511
if (propDef.hasSetter()) {
512-
JavaType propertyType = propDef.getSetter().getParameterType(0);
512+
AnnotatedMethod setter = propDef.getSetter();
513+
JavaType propertyType = setter.getParameterType(0);
513514
prop = constructSettableProperty(ctxt, beanDesc, propDef, propertyType);
515+
if (_isMergeableProperty(ctxt, setter, propertyType)) {
516+
AnnotatedMember accessor = propDef.getAccessor();
517+
if (accessor != null) {
518+
accessor.fixAccess(ctxt.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
519+
prop = MergingSettableBeanProperty.construct(prop, accessor);
520+
}
521+
}
514522
} else if (propDef.hasField()) {
515-
JavaType propertyType = propDef.getField().getType();
523+
AnnotatedField field = propDef.getField();
524+
JavaType propertyType = field.getType();
516525
prop = constructSettableProperty(ctxt, beanDesc, propDef, propertyType);
526+
if (_isMergeableProperty(ctxt, field, propertyType)) {
527+
AnnotatedMember accessor = propDef.getAccessor();
528+
if (accessor != null) {
529+
accessor.fixAccess(ctxt.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
530+
prop = MergingSettableBeanProperty.construct(prop, accessor);
531+
}
532+
}
517533
} else if (useGettersAsSetters && propDef.hasGetter()) {
518534
/* May also need to consider getters
519535
* for Map/Collection properties; but with lowest precedence
@@ -570,6 +586,22 @@ protected void addBeanProps(DeserializationContext ctxt,
570586
}
571587
}
572588

589+
protected boolean _isMergeableProperty(DeserializationContext ctxt,
590+
AnnotatedMember accessor, JavaType type)
591+
{
592+
AnnotationIntrospector ai = ctxt.getAnnotationIntrospector();
593+
if (ai != null) {
594+
JsonSetter.Value setter = ai.findSetterInfo(accessor);
595+
if (setter != null) {
596+
Boolean b = setter.getMerge();
597+
if (b != null) {
598+
return b.booleanValue();
599+
}
600+
}
601+
}
602+
return false;
603+
}
604+
573605
/**
574606
* Helper method called to filter out explicit ignored properties,
575607
* as well as properties that have "ignorable types".

src/main/java/com/fasterxml/jackson/databind/deser/SettableBeanProperty.java

+24-1
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,7 @@ public boolean hasValueDeserializer() {
371371
}
372372

373373
public boolean hasValueTypeDeserializer() { return (_valueTypeDeserializer != null); }
374-
374+
375375
public JsonDeserializer<Object> getValueDeserializer() {
376376
JsonDeserializer<Object> deser = _valueDeserializer;
377377
if (deser == MISSING_VALUE_DESERIALIZER) {
@@ -485,6 +485,29 @@ public final Object deserialize(JsonParser p, DeserializationContext ctxt) throw
485485
return _valueDeserializer.deserialize(p, ctxt);
486486
}
487487

488+
/**
489+
* @since 2.9
490+
*/
491+
public final Object deserializeWith(JsonParser p, DeserializationContext ctxt,
492+
Object toUpdate) throws IOException
493+
{
494+
JsonToken t = p.getCurrentToken();
495+
496+
// 20-Oct-2016, tatu: Not 100% sure what to do; probably best to simply return
497+
// null value and let caller decide what to do
498+
if (t == JsonToken.VALUE_NULL) {
499+
return _valueDeserializer.getNullValue(ctxt);
500+
}
501+
// 20-Oct-2016, tatu: Also tricky -- for now, report an erro
502+
if (_valueTypeDeserializer != null) {
503+
ctxt.reportBadDefinition(getType(),
504+
String.format("Can not merge polymorphic property '%s'",
505+
getName()));
506+
// return _valueDeserializer.deserializeWithType(p, ctxt, _valueTypeDeserializer);
507+
}
508+
return _valueDeserializer.deserialize(p, ctxt, toUpdate);
509+
}
510+
488511
/*
489512
/**********************************************************
490513
/* Helper methods
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
package com.fasterxml.jackson.databind.deser.impl;
22

33
import java.io.IOException;
4-
import java.lang.annotation.Annotation;
54

65
import com.fasterxml.jackson.core.JsonParser;
76
import com.fasterxml.jackson.databind.DeserializationContext;
8-
import com.fasterxml.jackson.databind.JsonDeserializer;
9-
import com.fasterxml.jackson.databind.PropertyName;
107
import com.fasterxml.jackson.databind.deser.*;
118
import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
129

@@ -19,65 +16,44 @@
1916
* @since 2.9
2017
*/
2118
public class MergingSettableBeanProperty
22-
extends SettableBeanProperty
19+
extends SettableBeanProperty.Delegating
2320
{
2421
private static final long serialVersionUID = 1L;
2522

2623
/**
27-
* Underlying actual property (field- or member-backed).
24+
* Member (field, method) used for accessing existing value.
2825
*/
29-
protected final SettableBeanProperty _delegate;
26+
protected final AnnotatedMember _accessor;
3027

3128
/*
3229
/**********************************************************
3330
/* Life-cycle
3431
/**********************************************************
3532
*/
3633

37-
public MergingSettableBeanProperty(SettableBeanProperty delegate)
34+
protected MergingSettableBeanProperty(SettableBeanProperty delegate,
35+
AnnotatedMember accessor)
3836
{
3937
super(delegate);
40-
_delegate = delegate;
38+
_accessor = accessor;
4139
}
4240

4341
protected MergingSettableBeanProperty(MergingSettableBeanProperty src,
4442
SettableBeanProperty delegate)
4543
{
46-
super(src);
47-
_delegate = src._delegate;
48-
}
49-
50-
@Override
51-
public SettableBeanProperty withValueDeserializer(JsonDeserializer<?> deser) {
52-
return _new(_delegate.withValueDeserializer(deser));
53-
}
54-
55-
@Override
56-
public SettableBeanProperty withName(PropertyName newName) {
57-
return _new(_delegate.withName(newName));
58-
}
59-
60-
protected MergingSettableBeanProperty _new(SettableBeanProperty newDelegate) {
61-
if (newDelegate == _delegate) {
62-
return this;
63-
}
64-
return new MergingSettableBeanProperty(this, newDelegate);
44+
super(delegate);
45+
_accessor = src._accessor;
6546
}
6647

67-
/*
68-
/**********************************************************
69-
/* BeanProperty impl
70-
/**********************************************************
71-
*/
72-
73-
@Override
74-
public AnnotatedMember getMember() {
75-
return _delegate.getMember();
48+
public static MergingSettableBeanProperty construct(SettableBeanProperty delegate,
49+
AnnotatedMember accessor)
50+
{
51+
return new MergingSettableBeanProperty(delegate, accessor);
7652
}
7753

7854
@Override
79-
public <A extends Annotation> A getAnnotation(Class<A> acls) {
80-
return _delegate.getAnnotation(acls);
55+
protected SettableBeanProperty withDelegate(SettableBeanProperty d) {
56+
return new MergingSettableBeanProperty(d, _accessor);
8157
}
8258

8359
/*
@@ -90,25 +66,39 @@ public <A extends Annotation> A getAnnotation(Class<A> acls) {
9066
public void deserializeAndSet(JsonParser p, DeserializationContext ctxt,
9167
Object instance) throws IOException
9268
{
93-
// TODO Auto-generated method stub
69+
delegate.set(instance, _deserialize(p, ctxt, instance));
9470
}
9571

9672
@Override
9773
public Object deserializeSetAndReturn(JsonParser p,
9874
DeserializationContext ctxt, Object instance) throws IOException {
99-
// TODO Auto-generated method stub
100-
return null;
75+
return delegate.setAndReturn(instance, _deserialize(p, ctxt, instance));
10176
}
10277

10378
@Override
10479
public void set(Object instance, Object value) throws IOException {
105-
// TODO Auto-generated method stub
80+
delegate.set(instance, value);
10681
}
10782

10883
@Override
10984
public Object setAndReturn(Object instance, Object value)
110-
throws IOException {
111-
// TODO Auto-generated method stub
112-
return null;
85+
throws IOException
86+
{
87+
return delegate.setAndReturn(instance, value);
88+
}
89+
90+
protected Object _deserialize(JsonParser p, DeserializationContext ctxt,
91+
Object instance) throws IOException
92+
{
93+
Object value = _accessor.getValue(instance);
94+
// 20-Oct-2016, tatu: Couple of possibilities of how to proceed; for
95+
// now, default to "normal" handling without merging
96+
if (value == null) {
97+
return delegate.deserialize(p, ctxt);
98+
}
99+
Object result = delegate.deserializeWith(p, ctxt, value);
100+
// 20-Oct-2016, tatu: Similarly, we may get same object or different one;
101+
// whether to return original or new is an open question.
102+
return result;
113103
}
114104
}

0 commit comments

Comments
 (0)