Skip to content

Commit 9036ebc

Browse files
committed
Implement #335
1 parent 1881327 commit 9036ebc

File tree

8 files changed

+123
-57
lines changed

8 files changed

+123
-57
lines changed

release-notes/VERSION

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ Version: 2.4.0 (xx-xxx-2014)
33

44
#88: Prevent use of type information for `JsonNode` via default typing
55
(reported by electricmonk@github)
6+
#335: Allow use of `@JsonProperytOrder(alphabetic=true)` for Map properties
67
#353: Problems with polymorphic types, `JsonNode` (related to #88)
78
(reported by cemo@github)
89
#369: Incorrect comparison for renaming in `POJOPropertyBuilder`

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -705,6 +705,11 @@ public String[] findSerializationPropertyOrder(AnnotatedClass ac) {
705705
* for which no explicit is defined should be alphabetically (lexicograpically)
706706
* ordered
707707
*/
708+
public Boolean findSerializationSortAlphabetically(Annotated ann) {
709+
return null;
710+
}
711+
712+
@Deprecated // since 2.4
708713
public Boolean findSerializationSortAlphabetically(AnnotatedClass ac) {
709714
return null;
710715
}

src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationIntrospectorPair.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,7 @@ public String[] findSerializationPropertyOrder(AnnotatedClass ac) {
509509
* ordered
510510
*/
511511
@Override
512+
@Deprecated
512513
public Boolean findSerializationSortAlphabetically(AnnotatedClass ac) {
513514
Boolean result = _primary.findSerializationSortAlphabetically(ac);
514515
if (result == null) {
@@ -517,6 +518,15 @@ public Boolean findSerializationSortAlphabetically(AnnotatedClass ac) {
517518
return result;
518519
}
519520

521+
@Override
522+
public Boolean findSerializationSortAlphabetically(Annotated ann) {
523+
Boolean result = _primary.findSerializationSortAlphabetically(ann);
524+
if (result == null) {
525+
result = _secondary.findSerializationSortAlphabetically(ann);
526+
}
527+
return result;
528+
}
529+
520530
// // // Serialization: property annotations
521531

522532
@Override

src/main/java/com/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -497,11 +497,21 @@ public String[] findSerializationPropertyOrder(AnnotatedClass ac) {
497497
}
498498

499499
@Override
500+
public Boolean findSerializationSortAlphabetically(Annotated ann) {
501+
return _findSortAlpha(ann);
502+
}
503+
504+
@Override
505+
@Deprecated
500506
public Boolean findSerializationSortAlphabetically(AnnotatedClass ac) {
501-
JsonPropertyOrder order = ac.getAnnotation(JsonPropertyOrder.class);
502-
return (order == null) ? null : order.alphabetic();
507+
return _findSortAlpha(ac);
503508
}
504509

510+
private final Boolean _findSortAlpha(Annotated ann) {
511+
JsonPropertyOrder order = ann.getAnnotation(JsonPropertyOrder.class);
512+
return (order == null) ? null : order.alphabetic();
513+
}
514+
505515
/*
506516
/**********************************************************
507517
/* Serialization: property annotations

src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ protected void _sortProperties()
285285
// Then how about explicit ordering?
286286
AnnotationIntrospector intr = _annotationIntrospector;
287287
boolean sort;
288-
Boolean alpha = (intr == null) ? null : intr.findSerializationSortAlphabetically(_classDef);
288+
Boolean alpha = (intr == null) ? null : intr.findSerializationSortAlphabetically((Annotated) _classDef);
289289

290290
if (alpha == null) {
291291
sort = _config.shouldSortPropertiesAlphabetically();

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

Lines changed: 51 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import com.fasterxml.jackson.core.*;
88
import com.fasterxml.jackson.databind.*;
99
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
10+
import com.fasterxml.jackson.databind.introspect.Annotated;
1011
import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
1112
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
1213
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonMapFormatVisitor;
@@ -85,6 +86,14 @@ public class MapSerializer
8586
*/
8687
protected final Object _filterId;
8788

89+
/**
90+
* Flag set if output is forced to be sorted by keys (usually due
91+
* to annotation).
92+
*
93+
* @since 2.4
94+
*/
95+
protected final boolean _sortKeys;
96+
8897
/*
8998
/**********************************************************
9099
/* Life-cycle
@@ -108,6 +117,7 @@ protected MapSerializer(HashSet<String> ignoredEntries,
108117
_dynamicValueSerializers = PropertySerializerMap.emptyMap();
109118
_property = null;
110119
_filterId = null;
120+
_sortKeys = false;
111121
}
112122

113123
@SuppressWarnings("unchecked")
@@ -126,6 +136,7 @@ protected MapSerializer(MapSerializer src, BeanProperty property,
126136
_dynamicValueSerializers = src._dynamicValueSerializers;
127137
_property = property;
128138
_filterId = src._filterId;
139+
_sortKeys = src._sortKeys;
129140
}
130141

131142
protected MapSerializer(MapSerializer src, TypeSerializer vts)
@@ -141,9 +152,10 @@ protected MapSerializer(MapSerializer src, TypeSerializer vts)
141152
_dynamicValueSerializers = src._dynamicValueSerializers;
142153
_property = src._property;
143154
_filterId = src._filterId;
155+
_sortKeys = src._sortKeys;
144156
}
145157

146-
protected MapSerializer(MapSerializer src, Object filterId)
158+
protected MapSerializer(MapSerializer src, Object filterId, boolean sortKeys)
147159
{
148160
super(Map.class, false);
149161
_ignoredEntries = src._ignoredEntries;
@@ -156,26 +168,39 @@ protected MapSerializer(MapSerializer src, Object filterId)
156168
_dynamicValueSerializers = src._dynamicValueSerializers;
157169
_property = src._property;
158170
_filterId = filterId;
171+
_sortKeys = sortKeys;
159172
}
160173

161174
@Override
162-
public MapSerializer _withValueTypeSerializer(TypeSerializer vts)
163-
{
175+
public MapSerializer _withValueTypeSerializer(TypeSerializer vts) {
164176
return new MapSerializer(this, vts);
165177
}
166178

179+
@Deprecated // since 2.3
180+
public MapSerializer withResolved(BeanProperty property,
181+
JsonSerializer<?> keySerializer, JsonSerializer<?> valueSerializer, HashSet<String> ignored) {
182+
return withResolved(property, keySerializer, valueSerializer, ignored, _sortKeys);
183+
}
184+
185+
/**
186+
* @since 2.4
187+
*/
167188
public MapSerializer withResolved(BeanProperty property,
168189
JsonSerializer<?> keySerializer, JsonSerializer<?> valueSerializer,
169-
HashSet<String> ignored)
190+
HashSet<String> ignored, boolean sortKeys)
170191
{
171-
return new MapSerializer(this, property, keySerializer, valueSerializer, ignored);
192+
MapSerializer ser = new MapSerializer(this, property, keySerializer, valueSerializer, ignored);
193+
if (sortKeys != ser._sortKeys) {
194+
ser = new MapSerializer(ser, _filterId, sortKeys);
195+
}
196+
return ser;
172197
}
173-
198+
174199
/**
175200
* @since 2.3
176201
*/
177202
public MapSerializer withFilterId(Object filterId) {
178-
return (_filterId == filterId) ? this : new MapSerializer(this, filterId);
203+
return (_filterId == filterId) ? this : new MapSerializer(this, filterId, _sortKeys);
179204
}
180205

181206
/**
@@ -252,21 +277,18 @@ public JsonSerializer<?> createContextual(SerializerProvider provider,
252277
*/
253278
JsonSerializer<?> ser = null;
254279
JsonSerializer<?> keySer = null;
280+
final AnnotationIntrospector intr = provider.getAnnotationIntrospector();
281+
final AnnotatedMember propertyAcc = (property == null) ? null : property.getMember();
255282

256283
// First: if we have a property, may have property-annotation overrides
257-
if (property != null) {
258-
AnnotatedMember m = property.getMember();
259-
if (m != null) {
260-
Object serDef;
261-
final AnnotationIntrospector intr = provider.getAnnotationIntrospector();
262-
serDef = intr.findKeySerializer(m);
263-
if (serDef != null) {
264-
keySer = provider.serializerInstance(m, serDef);
265-
}
266-
serDef = intr.findContentSerializer(m);
267-
if (serDef != null) {
268-
ser = provider.serializerInstance(m, serDef);
269-
}
284+
if (propertyAcc != null && intr != null) {
285+
Object serDef = intr.findKeySerializer(propertyAcc);
286+
if (serDef != null) {
287+
keySer = provider.serializerInstance(propertyAcc, serDef);
288+
}
289+
serDef = intr.findContentSerializer(propertyAcc);
290+
if (serDef != null) {
291+
ser = provider.serializerInstance(propertyAcc, serDef);
270292
}
271293
}
272294
if (ser == null) {
@@ -293,18 +315,20 @@ public JsonSerializer<?> createContextual(SerializerProvider provider,
293315
} else {
294316
keySer = provider.handleSecondaryContextualization(keySer, property);
295317
}
296-
HashSet<String> ignored = this._ignoredEntries;
297-
AnnotationIntrospector intr = provider.getAnnotationIntrospector();
298-
if (intr != null && property != null) {
299-
String[] moreToIgnore = intr.findPropertiesToIgnore(property.getMember());
318+
HashSet<String> ignored = _ignoredEntries;
319+
boolean sortKeys = false;
320+
if (intr != null && propertyAcc != null) {
321+
String[] moreToIgnore = intr.findPropertiesToIgnore(propertyAcc);
300322
if (moreToIgnore != null) {
301323
ignored = (ignored == null) ? new HashSet<String>() : new HashSet<String>(ignored);
302324
for (String str : moreToIgnore) {
303325
ignored.add(str);
304326
}
305327
}
328+
Boolean b = intr.findSerializationSortAlphabetically(propertyAcc);
329+
sortKeys = (b != null) && b.booleanValue();
306330
}
307-
MapSerializer mser = withResolved(property, keySer, ser, ignored);
331+
MapSerializer mser = withResolved(property, keySer, ser, ignored, sortKeys);
308332

309333
// [Issue#307]: allow filtering
310334
if (property != null) {
@@ -379,7 +403,7 @@ public void serialize(Map<?,?> value, JsonGenerator jgen, SerializerProvider pro
379403
findPropertyFilter(provider, _filterId, value));
380404
return;
381405
}
382-
if (provider.isEnabled(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS)) {
406+
if (_sortKeys || provider.isEnabled(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS)) {
383407
value = _orderEntries(value);
384408
}
385409
if (_valueSerializer != null) {
@@ -398,7 +422,7 @@ public void serializeWithType(Map<?,?> value, JsonGenerator jgen, SerializerProv
398422
{
399423
typeSer.writeTypePrefixForObject(value, jgen);
400424
if (!value.isEmpty()) {
401-
if (provider.isEnabled(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS)) {
425+
if (_sortKeys || provider.isEnabled(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS)) {
402426
value = _orderEntries(value);
403427
}
404428
if (_valueSerializer != null) {

src/test/java/com/fasterxml/jackson/databind/deser/TestParentChildReferences.java

Lines changed: 19 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -198,18 +198,19 @@ static class Photo708 extends Model708 {
198198
/* Unit tests
199199
/**********************************************************
200200
*/
201+
202+
private final ObjectMapper MAPPER = objectMapper();
201203

202204
public void testSimpleRefs() throws Exception
203205
{
204206
SimpleTreeNode root = new SimpleTreeNode("root");
205207
SimpleTreeNode child = new SimpleTreeNode("kid");
206-
ObjectMapper mapper = new ObjectMapper();
207208
root.child = child;
208209
child.parent = root;
209210

210-
String json = mapper.writeValueAsString(root);
211+
String json = MAPPER.writeValueAsString(root);
211212

212-
SimpleTreeNode resultNode = mapper.readValue(json, SimpleTreeNode.class);
213+
SimpleTreeNode resultNode = MAPPER.readValue(json, SimpleTreeNode.class);
213214
assertEquals("root", resultNode.name);
214215
SimpleTreeNode resultChild = resultNode.child;
215216
assertNotNull(resultChild);
@@ -222,13 +223,12 @@ public void testSimpleRefsWithGetter() throws Exception
222223
{
223224
SimpleTreeNode2 root = new SimpleTreeNode2("root");
224225
SimpleTreeNode2 child = new SimpleTreeNode2("kid");
225-
ObjectMapper mapper = new ObjectMapper();
226226
root.child = child;
227227
child.parent = root;
228228

229-
String json = mapper.writeValueAsString(root);
229+
String json = MAPPER.writeValueAsString(root);
230230

231-
SimpleTreeNode2 resultNode = mapper.readValue(json, SimpleTreeNode2.class);
231+
SimpleTreeNode2 resultNode = MAPPER.readValue(json, SimpleTreeNode2.class);
232232
assertEquals("root", resultNode.name);
233233
SimpleTreeNode2 resultChild = resultNode.child;
234234
assertNotNull(resultChild);
@@ -241,15 +241,14 @@ public void testFullRefs() throws Exception
241241
FullTreeNode root = new FullTreeNode("root");
242242
FullTreeNode child1 = new FullTreeNode("kid1");
243243
FullTreeNode child2 = new FullTreeNode("kid2");
244-
ObjectMapper mapper = new ObjectMapper();
245244
root.firstChild = child1;
246245
child1.parent = root;
247246
child1.next = child2;
248247
child2.prev = child1;
249248

250-
String json = mapper.writeValueAsString(root);
249+
String json = MAPPER.writeValueAsString(root);
251250

252-
FullTreeNode resultNode = mapper.readValue(json, FullTreeNode.class);
251+
FullTreeNode resultNode = MAPPER.readValue(json, FullTreeNode.class);
253252
assertEquals("root", resultNode.name);
254253
FullTreeNode resultChild = resultNode.firstChild;
255254
assertNotNull(resultChild);
@@ -271,10 +270,9 @@ public void testArrayOfRefs() throws Exception
271270
ArrayNode node1 = new ArrayNode("a");
272271
ArrayNode node2 = new ArrayNode("b");
273272
root.nodes = new ArrayNode[] { node1, node2 };
274-
ObjectMapper mapper = new ObjectMapper();
275-
String json = mapper.writeValueAsString(root);
273+
String json = MAPPER.writeValueAsString(root);
276274

277-
NodeArray result = mapper.readValue(json, NodeArray.class);
275+
NodeArray result = MAPPER.readValue(json, NodeArray.class);
278276
ArrayNode[] kids = result.nodes;
279277
assertNotNull(kids);
280278
assertEquals(2, kids.length);
@@ -290,10 +288,9 @@ public void testListOfRefs() throws Exception
290288
NodeForList node1 = new NodeForList("a");
291289
NodeForList node2 = new NodeForList("b");
292290
root.nodes = Arrays.asList(node1, node2);
293-
ObjectMapper mapper = new ObjectMapper();
294-
String json = mapper.writeValueAsString(root);
291+
String json = MAPPER.writeValueAsString(root);
295292

296-
NodeList result = mapper.readValue(json, NodeList.class);
293+
NodeList result = MAPPER.readValue(json, NodeList.class);
297294
List<NodeForList> kids = result.nodes;
298295
assertNotNull(kids);
299296
assertEquals(2, kids.size());
@@ -312,10 +309,9 @@ public void testMapOfRefs() throws Exception
312309
nodes.put("a1", node1);
313310
nodes.put("b2", node2);
314311
root.nodes = nodes;
315-
ObjectMapper mapper = new ObjectMapper();
316-
String json = mapper.writeValueAsString(root);
312+
String json = MAPPER.writeValueAsString(root);
317313

318-
NodeMap result = mapper.readValue(json, NodeMap.class);
314+
NodeMap result = MAPPER.readValue(json, NodeMap.class);
319315
Map<String,NodeForMap> kids = result.nodes;
320316
assertNotNull(kids);
321317
assertEquals(2, kids.size());
@@ -336,10 +332,9 @@ public void testAbstract368() throws Exception
336332
child.prev = parent;
337333

338334
// serialization ought to be ok
339-
ObjectMapper mapper = new ObjectMapper();
340-
String json = mapper.writeValueAsString(parent);
335+
String json = MAPPER.writeValueAsString(parent);
341336

342-
AbstractNode root = mapper.readValue(json, AbstractNode.class);
337+
AbstractNode root = MAPPER.readValue(json, AbstractNode.class);
343338

344339
assertEquals(ConcreteNode.class, root.getClass());
345340
assertEquals("p", root.id);
@@ -355,18 +350,16 @@ public void testIssue693() throws Exception
355350
Parent parent = new Parent();
356351
parent.addChild(new Child("foo"));
357352
parent.addChild(new Child("bar"));
358-
ObjectMapper mapper = new ObjectMapper();
359-
byte[] bytes = mapper.writeValueAsBytes(parent);
360-
Parent value = mapper.readValue(bytes, Parent.class);
353+
byte[] bytes = MAPPER.writeValueAsBytes(parent);
354+
Parent value = MAPPER.readValue(bytes, Parent.class);
361355
for (Child child : value.children) {
362356
assertEquals(value, child.getParent());
363357
}
364358
}
365359

366360
public void testIssue708() throws Exception
367361
{
368-
ObjectMapper mapper = new ObjectMapper();
369-
Advertisement708 ad = mapper.readValue("{\"title\":\"Hroch\",\"photos\":[{\"id\":3}]}", Advertisement708.class);
362+
Advertisement708 ad = MAPPER.readValue("{\"title\":\"Hroch\",\"photos\":[{\"id\":3}]}", Advertisement708.class);
370363
assertNotNull(ad);
371364
}
372365
}

0 commit comments

Comments
 (0)