Skip to content

Commit d1f58c6

Browse files
committed
Implement #522, give annotation access through PropertyWriter (similar to what BeanPropertyWriter had)
1 parent 1f06e8f commit d1f58c6

File tree

5 files changed

+171
-24
lines changed

5 files changed

+171
-24
lines changed

src/main/java/com/fasterxml/jackson/databind/ser/BeanPropertyWriter.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,12 +320,14 @@ public void setNonTrivialBaseType(JavaType t) {
320320
@Override public PropertyName getWrapperName() { return _wrapperName; }
321321
@Override public boolean isRequired() { return _metadata.isRequired(); }
322322
@Override public PropertyMetadata getMetadata() { return _metadata; }
323-
323+
324+
// Note: also part of 'PropertyWriter'
324325
@Override
325326
public <A extends Annotation> A getAnnotation(Class<A> acls) {
326327
return _member.getAnnotation(acls);
327328
}
328329

330+
// Note: also part of 'PropertyWriter'
329331
@Override
330332
public <A extends Annotation> A getContextAnnotation(Class<A> acls) {
331333
return _contextAnnotations.get(acls);

src/main/java/com/fasterxml/jackson/databind/ser/PropertyWriter.java

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
package com.fasterxml.jackson.databind.ser;
22

3-
import com.fasterxml.jackson.core.JsonGenerator;
3+
import java.lang.annotation.Annotation;
44

5+
import com.fasterxml.jackson.core.JsonGenerator;
56
import com.fasterxml.jackson.databind.*;
67
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor;
78
import com.fasterxml.jackson.databind.node.ObjectNode;
@@ -25,7 +26,52 @@ public abstract class PropertyWriter
2526
public abstract String getName();
2627

2728
public abstract PropertyName getFullName();
29+
30+
/**
31+
* Convenience method for accessing annotation that may be associated
32+
* either directly on property, or, if not, via enclosing class (context).
33+
* This allows adding baseline contextual annotations, for example, by adding
34+
* an annotation for a given class and making that apply to all properties
35+
* unless overridden by per-property annotations.
36+
*<p>
37+
* This method is functionally equivalent to:
38+
*<pre>
39+
* MyAnnotation ann = propWriter.getAnnotation(MyAnnotation.class);
40+
* if (ann == null) {
41+
* ann = propWriter.getContextAnnotation(MyAnnotation.class);
42+
* }
43+
*</pre>
44+
* that is, tries to find a property annotation first, but if one is not
45+
* found, tries to find context-annotation (from enclosing class) of
46+
* same type.
47+
*
48+
* @since 2.5
49+
*/
50+
public <A extends Annotation> A findAnnotation(Class<A> acls) {
51+
A ann = getAnnotation(acls);
52+
if (ann == null) {
53+
ann = getContextAnnotation(acls);
54+
}
55+
return ann;
56+
}
2857

58+
/**
59+
* Method for accessing annotations directly declared for property that this
60+
* writer is associated with.
61+
*
62+
* @since 2.5
63+
*/
64+
public abstract <A extends Annotation> A getAnnotation(Class<A> acls);
65+
66+
/**
67+
* Method for accessing annotations declared in context of the property that this
68+
* writer is associated with; usually this means annotations on enclosing class
69+
* for property.
70+
*
71+
* @since 2.5
72+
*/
73+
public abstract <A extends Annotation> A getContextAnnotation(Class<A> acls);
74+
2975
/*
3076
/**********************************************************
3177
/* Serialization methods, regular output
@@ -35,15 +81,15 @@ public abstract class PropertyWriter
3581
/**
3682
* The main serialization method called by filter when property is to be written normally.
3783
*/
38-
public abstract void serializeAsField(Object pojo, JsonGenerator jgen, SerializerProvider prov)
84+
public abstract void serializeAsField(Object value, JsonGenerator jgen, SerializerProvider provider)
3985
throws Exception;
4086

4187
/**
4288
* Serialization method that filter needs to call in cases where property is to be
4389
* filtered, but the underlying data format requires a placeholder of some kind.
4490
* This is usually the case for tabular (positional) data formats such as CSV.
4591
*/
46-
public abstract void serializeAsOmittedField(Object pojo, JsonGenerator jgen, SerializerProvider prov)
92+
public abstract void serializeAsOmittedField(Object value, JsonGenerator jgen, SerializerProvider provider)
4793
throws Exception;
4894

4995
/*
@@ -62,15 +108,15 @@ public abstract void serializeAsOmittedField(Object pojo, JsonGenerator jgen, Se
62108
* data format; so it is typically NOT called for fully tabular formats such as CSV,
63109
* where logical output is still as form of POJOs.
64110
*/
65-
public abstract void serializeAsElement(Object pojo, JsonGenerator jgen, SerializerProvider prov)
111+
public abstract void serializeAsElement(Object value, JsonGenerator jgen, SerializerProvider provider)
66112
throws Exception;
67113

68114
/**
69115
* Serialization method called when doing tabular (positional) output from databind,
70116
* but then value is to be omitted. This requires output of a placeholder value
71117
* of some sort; often similar to {@link #serializeAsOmittedField}.
72118
*/
73-
public abstract void serializeAsPlaceholder(Object pojo, JsonGenerator jgen, SerializerProvider prov)
119+
public abstract void serializeAsPlaceholder(Object value, JsonGenerator jgen, SerializerProvider provider)
74120
throws Exception;
75121

76122
/*

src/main/java/com/fasterxml/jackson/databind/ser/std/MapProperty.java

Lines changed: 48 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package com.fasterxml.jackson.databind.ser.std;
22

33
import java.io.IOException;
4+
import java.lang.annotation.Annotation;
45

56
import com.fasterxml.jackson.core.JsonGenerator;
7+
import com.fasterxml.jackson.databind.BeanProperty;
68
import com.fasterxml.jackson.databind.JsonMappingException;
79
import com.fasterxml.jackson.databind.JsonSerializer;
810
import com.fasterxml.jackson.databind.PropertyName;
@@ -20,26 +22,48 @@
2022
*/
2123
public class MapProperty extends PropertyWriter
2224
{
23-
protected TypeSerializer _typeSerializer;
24-
25-
protected Object _key, _value;
25+
protected final TypeSerializer _typeSerializer;
26+
27+
protected final BeanProperty _property;
28+
29+
protected Object _key;
2630

2731
protected JsonSerializer<Object> _keySerializer, _valueSerializer;
2832

29-
public MapProperty(TypeSerializer typeSer)
33+
/**
34+
* @deprecated since 2.4
35+
*/
36+
@Deprecated // since 2.4
37+
public MapProperty(TypeSerializer typeSer) {
38+
this(typeSer, null);
39+
}
40+
41+
public MapProperty(TypeSerializer typeSer, BeanProperty prop)
3042
{
3143
_typeSerializer = typeSer;
44+
_property = prop;
45+
}
46+
47+
/**
48+
* Deprecated method with wrong signature; value should not be assigned
49+
* to property, should be passed via proper call-through methods.
50+
*
51+
* @deprecated Since 2.5, remove in 2.6
52+
*/
53+
@Deprecated // since 2.5
54+
public void reset(Object key, Object value,
55+
JsonSerializer<Object> keySer, JsonSerializer<Object> valueSer) {
56+
reset(key, keySer, valueSer);
3257
}
3358

3459
/**
3560
* Initialization method that needs to be called before passing
3661
* property to filter.
3762
*/
38-
public void reset(Object key, Object value,
63+
public void reset(Object key,
3964
JsonSerializer<Object> keySer, JsonSerializer<Object> valueSer)
4065
{
4166
_key = key;
42-
_value = value;
4367
_keySerializer = keySer;
4468
_valueSerializer = valueSer;
4569
}
@@ -58,19 +82,29 @@ public PropertyName getFullName() {
5882
}
5983

6084
@Override
61-
public void serializeAsField(Object pojo, JsonGenerator jgen,
85+
public <A extends Annotation> A getAnnotation(Class<A> acls) {
86+
return (_property == null) ? null : _property.getAnnotation(acls);
87+
}
88+
89+
@Override
90+
public <A extends Annotation> A getContextAnnotation(Class<A> acls) {
91+
return (_property == null) ? null : _property.getContextAnnotation(acls);
92+
}
93+
94+
@Override
95+
public void serializeAsField(Object value, JsonGenerator jgen,
6296
SerializerProvider provider) throws IOException
6397
{
6498
_keySerializer.serialize(_key, jgen, provider);
6599
if (_typeSerializer == null) {
66-
_valueSerializer.serialize(_value, jgen, provider);
100+
_valueSerializer.serialize(value, jgen, provider);
67101
} else {
68-
_valueSerializer.serializeWithType(_value, jgen, provider, _typeSerializer);
102+
_valueSerializer.serializeWithType(value, jgen, provider, _typeSerializer);
69103
}
70104
}
71105

72106
@Override
73-
public void serializeAsOmittedField(Object pojo, JsonGenerator jgen,
107+
public void serializeAsOmittedField(Object value, JsonGenerator jgen,
74108
SerializerProvider provider) throws Exception
75109
{
76110
if (!jgen.canOmitFields()) {
@@ -79,18 +113,18 @@ public void serializeAsOmittedField(Object pojo, JsonGenerator jgen,
79113
}
80114

81115
@Override
82-
public void serializeAsElement(Object pojo, JsonGenerator jgen,
116+
public void serializeAsElement(Object value, JsonGenerator jgen,
83117
SerializerProvider provider) throws Exception
84118
{
85119
if (_typeSerializer == null) {
86-
_valueSerializer.serialize(_value, jgen, provider);
120+
_valueSerializer.serialize(value, jgen, provider);
87121
} else {
88-
_valueSerializer.serializeWithType(_value, jgen, provider, _typeSerializer);
122+
_valueSerializer.serializeWithType(value, jgen, provider, _typeSerializer);
89123
}
90124
}
91125

92126
@Override
93-
public void serializeAsPlaceholder(Object pojo, JsonGenerator jgen,
127+
public void serializeAsPlaceholder(Object value, JsonGenerator jgen,
94128
SerializerProvider provider) throws Exception
95129
{
96130
jgen.writeNull();

src/main/java/com/fasterxml/jackson/databind/ser/std/MapSerializer.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -557,7 +557,7 @@ public void serializeFilteredFields(Map<?,?> value, JsonGenerator jgen, Serializ
557557
final boolean skipNulls = !provider.isEnabled(SerializationFeature.WRITE_NULL_MAP_VALUES);
558558

559559
PropertySerializerMap serializers = _dynamicValueSerializers;
560-
final MapProperty prop = new MapProperty(_valueTypeSerializer);
560+
final MapProperty prop = new MapProperty(_valueTypeSerializer, _property);
561561

562562
for (Map.Entry<?,?> entry : value.entrySet()) {
563563
// First, serialize key
@@ -590,9 +590,9 @@ public void serializeFilteredFields(Map<?,?> value, JsonGenerator jgen, Serializ
590590
serializers = _dynamicValueSerializers;
591591
}
592592
}
593-
prop.reset(keyElem, valueElem, keySer, valueSer);
593+
prop.reset(keyElem, keySer, valueSer);
594594
try {
595-
filter.serializeAsField(value, jgen, provider, prop);
595+
filter.serializeAsField(valueElem, jgen, provider, prop);
596596
} catch (Exception e) {
597597
// [JACKSON-55] Need to add reference information
598598
String keyDesc = ""+keyElem;

src/test/java/com/fasterxml/jackson/databind/filter/TestMapFiltering.java

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,38 @@
11
package com.fasterxml.jackson.databind.filter;
22

3+
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Retention;
5+
import java.lang.annotation.RetentionPolicy;
6+
import java.lang.annotation.Target;
37
import java.util.*;
48

59
import com.fasterxml.jackson.annotation.JsonFilter;
10+
import com.fasterxml.jackson.core.JsonGenerator;
611
import com.fasterxml.jackson.databind.*;
12+
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor;
13+
import com.fasterxml.jackson.databind.node.ObjectNode;
714
import com.fasterxml.jackson.databind.ser.FilterProvider;
15+
import com.fasterxml.jackson.databind.ser.PropertyFilter;
16+
import com.fasterxml.jackson.databind.ser.PropertyWriter;
817
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
918
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
1019

1120
public class TestMapFiltering extends BaseMapTest
1221
{
22+
@Target({ElementType.FIELD})
23+
@Retention(RetentionPolicy.RUNTIME)
24+
public @interface CustomOffset
25+
{
26+
public int value();
27+
}
28+
1329
@SuppressWarnings("serial")
1430
@JsonFilter("filterForMaps")
1531
static class FilteredBean extends LinkedHashMap<String,Integer> { }
1632

1733
static class MapBean {
1834
@JsonFilter("filterX")
35+
@CustomOffset(1)
1936
public Map<String,Integer> values;
2037

2138
public MapBean() {
@@ -25,7 +42,46 @@ public MapBean() {
2542
values.put("c", 9);
2643
}
2744
}
28-
45+
46+
static class MyMapFilter implements PropertyFilter
47+
{
48+
@Override
49+
public void serializeAsField(Object value, JsonGenerator jgen,
50+
SerializerProvider provider, PropertyWriter writer)
51+
throws Exception
52+
{
53+
String name = writer.getName();
54+
if (!"a".equals(name)) {
55+
return;
56+
}
57+
CustomOffset n = writer.findAnnotation(CustomOffset.class);
58+
int offset = (n == null) ? 0 : n.value();
59+
Integer I = offset + ((Integer) value).intValue();
60+
61+
writer.serializeAsField(I, jgen, provider);
62+
}
63+
64+
@Override
65+
public void serializeAsElement(Object elementValue, JsonGenerator jgen,
66+
SerializerProvider prov, PropertyWriter writer)
67+
throws Exception {
68+
// not needed for testing
69+
}
70+
71+
@Override
72+
public void depositSchemaProperty(PropertyWriter writer,
73+
ObjectNode propertiesNode, SerializerProvider provider)
74+
throws JsonMappingException {
75+
76+
}
77+
78+
@Override
79+
public void depositSchemaProperty(PropertyWriter writer,
80+
JsonObjectFormatVisitor objectVisitor,
81+
SerializerProvider provider) throws JsonMappingException {
82+
}
83+
}
84+
2985
/*
3086
/**********************************************************
3187
/* Unit tests
@@ -53,4 +109,13 @@ public void testMapFilteringViaClass() throws Exception
53109
assertEquals(aposToQuotes("{'b':3}"), json);
54110
}
55111

112+
// [Issue#522]
113+
public void testMapFilteringWithAnnotations() throws Exception
114+
{
115+
FilterProvider prov = new SimpleFilterProvider().addFilter("filterX",
116+
new MyMapFilter());
117+
String json = MAPPER.writer(prov).writeValueAsString(new MapBean());
118+
// a=1 should become a=2
119+
assertEquals(aposToQuotes("{'values':{'a':2}}"), json);
120+
}
56121
}

0 commit comments

Comments
 (0)