Skip to content

Commit dcf82b7

Browse files
authored
FasterXML/jackson-databind#1296 @JsonIncludeProperties (#174)
Add `@JsonIncludeProperties`
1 parent 8dac7ce commit dcf82b7

File tree

2 files changed

+235
-0
lines changed

2 files changed

+235
-0
lines changed
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
package com.fasterxml.jackson.annotation;
2+
3+
import com.fasterxml.jackson.annotation.JsonInclude.Include;
4+
5+
import javax.swing.text.html.HTMLDocument;
6+
import java.lang.annotation.ElementType;
7+
import java.lang.annotation.Retention;
8+
import java.lang.annotation.RetentionPolicy;
9+
import java.lang.annotation.Target;
10+
import java.util.Collections;
11+
import java.util.HashSet;
12+
import java.util.Iterator;
13+
import java.util.Set;
14+
15+
/**
16+
* Annotation that can be used to either only include serialization of
17+
* properties (during serialization), or only include processing of
18+
* JSON properties read (during deserialization).
19+
* <p>
20+
* Example:
21+
* <pre>
22+
* // to only include specified fields from being serialized or deserialized
23+
* // (i.e. only include in JSON output; or being set even if they were included)
24+
* &#064;JsonIncludeProperties({ "internalId", "secretKey" })
25+
* </pre>
26+
* <p>
27+
* Annotation can be applied both to classes and
28+
* to properties. If used for both, actual set will be union of all
29+
* includes: that is, you can only add properties to include, not remove
30+
* or override. So you can not remove properties to include using
31+
* per-property annotation.
32+
*
33+
* @since 2.12
34+
*/
35+
@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE,
36+
ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD})
37+
@Retention(RetentionPolicy.RUNTIME)
38+
@JacksonAnnotation
39+
public @interface JsonIncludeProperties
40+
{
41+
/**
42+
* Names of properties to include.
43+
*/
44+
public String[] value() default {};
45+
46+
/*
47+
/**********************************************************
48+
/* Value class used to enclose information, allow for
49+
/* merging of layered configuration settings.
50+
/**********************************************************
51+
*/
52+
53+
/**
54+
* Helper class used to contain information from a single {@link JsonIncludeProperties}
55+
* annotation, as well as to provide possible overrides from non-annotation sources.
56+
*
57+
* @since 2.12
58+
*/
59+
public static class Value implements JacksonAnnotationValue<JsonIncludeProperties>, java.io.Serializable
60+
{
61+
private static final long serialVersionUID = 1L;
62+
63+
/**
64+
* Default instance has no explicitly included fields
65+
*/
66+
protected final static JsonIncludeProperties.Value ALL = new JsonIncludeProperties.Value(null);
67+
68+
/**
69+
* Name of the properties to include.
70+
* Null means that all properties are included, empty means none.
71+
*/
72+
protected final Set<String> _included;
73+
74+
protected Value(Set<String> included)
75+
{
76+
_included = included;
77+
}
78+
79+
public static JsonIncludeProperties.Value from(JsonIncludeProperties src)
80+
{
81+
if (src == null) {
82+
return ALL;
83+
}
84+
85+
return new Value(_asSet(src.value()));
86+
}
87+
88+
public static JsonIncludeProperties.Value all()
89+
{
90+
return ALL;
91+
}
92+
93+
@Override
94+
public Class<JsonIncludeProperties> valueFor()
95+
{
96+
return JsonIncludeProperties.class;
97+
}
98+
99+
public Set<String> getIncluded()
100+
{
101+
return _included;
102+
}
103+
104+
/**
105+
* Mutant factory method to override the current value with an another, merging the included fields.
106+
*/
107+
public JsonIncludeProperties.Value withOverrides(JsonIncludeProperties.Value overrides) {
108+
if (overrides == null || overrides.getIncluded() == null) {
109+
return this;
110+
}
111+
112+
if (_included == null) {
113+
return overrides;
114+
}
115+
116+
HashSet<String> included = new HashSet<String>(_included);
117+
Iterator<String> iterator = included.iterator();
118+
while (iterator.hasNext()) {
119+
if (!overrides.getIncluded().contains(iterator.next())) {
120+
iterator.remove();
121+
}
122+
}
123+
124+
return new JsonIncludeProperties.Value(new HashSet<String>(included));
125+
}
126+
127+
@Override
128+
public String toString() {
129+
return String.format("JsonIncludeProperties.Value(included=%s)",
130+
_included);
131+
}
132+
133+
@Override
134+
public int hashCode() {
135+
return (_included == null ? 0 : _included.size())
136+
;
137+
}
138+
139+
@Override
140+
public boolean equals(Object o) {
141+
if (o == this) return true;
142+
if (o == null) return false;
143+
return (o.getClass() == getClass())
144+
&& _equals(this, (Value) o);
145+
}
146+
147+
private static boolean _equals(Value a, Value b)
148+
{
149+
return a._included == null ? b._included == null :
150+
// keep this last just because it can be expensive
151+
a._included.equals(b._included)
152+
;
153+
}
154+
155+
private static Set<String> _asSet(String[] v)
156+
{
157+
if (v == null || v.length == 0) {
158+
return Collections.emptySet();
159+
}
160+
Set<String> s = new HashSet<String>(v.length);
161+
for (String str : v) {
162+
s.add(str);
163+
}
164+
return s;
165+
}
166+
}
167+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package com.fasterxml.jackson.annotation;
2+
3+
import java.util.Arrays;
4+
import java.util.Collections;
5+
import java.util.LinkedHashSet;
6+
import java.util.Set;
7+
8+
/**
9+
* Tests to verify that it is possibly to merge {@link JsonIncludeProperties.Value}
10+
* instances for overrides
11+
*/
12+
public class JsonIncludePropertiesTest extends TestBase
13+
{
14+
@JsonIncludeProperties(value = {"foo", "bar"})
15+
private final static class Bogus
16+
{
17+
}
18+
19+
private final JsonIncludeProperties.Value ALL = JsonIncludeProperties.Value.all();
20+
21+
public void testAll()
22+
{
23+
assertSame(ALL, JsonIncludeProperties.Value.from(null));
24+
assertNull(ALL.getIncluded());
25+
assertEquals(ALL, ALL);
26+
assertEquals("JsonIncludeProperties.Value(included=null)", ALL.toString());
27+
assertEquals(0, ALL.hashCode());
28+
}
29+
30+
public void testFromAnnotation()
31+
{
32+
JsonIncludeProperties.Value v = JsonIncludeProperties.Value.from(Bogus.class.getAnnotation(JsonIncludeProperties.class));
33+
assertNotNull(v);
34+
Set<String> included = v.getIncluded();
35+
assertEquals(2, v.getIncluded().size());
36+
assertEquals(_set("foo", "bar"), included);
37+
assertEquals("JsonIncludeProperties.Value(included=[bar, foo])", v.toString());
38+
assertEquals(v, JsonIncludeProperties.Value.from(Bogus.class.getAnnotation(JsonIncludeProperties.class)));
39+
}
40+
41+
public void testWithOverridesAll() {
42+
JsonIncludeProperties.Value v = JsonIncludeProperties.Value.from(Bogus.class.getAnnotation(JsonIncludeProperties.class));
43+
v = v.withOverrides(ALL);
44+
Set<String> included = v.getIncluded();
45+
assertEquals(2, included.size());
46+
assertEquals(_set("foo", "bar"), included);
47+
}
48+
49+
public void testWithOverridesEmpty() {
50+
JsonIncludeProperties.Value v = JsonIncludeProperties.Value.from(Bogus.class.getAnnotation(JsonIncludeProperties.class));
51+
v = v.withOverrides(new JsonIncludeProperties.Value(Collections.<String>emptySet()));
52+
Set<String> included = v.getIncluded();
53+
assertEquals(0, included.size());
54+
}
55+
56+
public void testWithOverridesMerge() {
57+
JsonIncludeProperties.Value v = JsonIncludeProperties.Value.from(Bogus.class.getAnnotation(JsonIncludeProperties.class));
58+
v = v.withOverrides(new JsonIncludeProperties.Value(_set("foo")));
59+
Set<String> included = v.getIncluded();
60+
assertEquals(1, included.size());
61+
assertEquals(_set("foo"), included);
62+
}
63+
64+
private Set<String> _set(String... args)
65+
{
66+
return new LinkedHashSet<String>(Arrays.asList(args));
67+
}
68+
}

0 commit comments

Comments
 (0)