You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by dl...@apache.org on 2017/08/25 11:22:46 UTC
svn commit: r1806158 - in /felix/trunk/converter/serializer/src:
main/java/org/apache/felix/serializer/impl/json/
main/java/org/apache/felix/serializer/impl/yaml/
main/java/org/osgi/service/serializer/
test/java/org/apache/felix/serializer/impl/json/
Author: dleangen
Date: Fri Aug 25 11:22:45 2017
New Revision: 1806158
URL: http://svn.apache.org/viewvc?rev=1806158&view=rev
Log:
[FELIX-5412] Added ability to determine the order of the keys to help with readability and debugging
Modified:
felix/trunk/converter/serializer/src/main/java/org/apache/felix/serializer/impl/json/DebugJsonWriter.java
felix/trunk/converter/serializer/src/main/java/org/apache/felix/serializer/impl/json/DefaultJsonWriter.java
felix/trunk/converter/serializer/src/main/java/org/apache/felix/serializer/impl/json/JsonWriterFactory.java
felix/trunk/converter/serializer/src/main/java/org/apache/felix/serializer/impl/yaml/YamlWriterFactory.java
felix/trunk/converter/serializer/src/main/java/org/osgi/service/serializer/WriterFactory.java
felix/trunk/converter/serializer/src/test/java/org/apache/felix/serializer/impl/json/JsonBackingObjectSerializationTest.java
Modified: felix/trunk/converter/serializer/src/main/java/org/apache/felix/serializer/impl/json/DebugJsonWriter.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/serializer/src/main/java/org/apache/felix/serializer/impl/json/DebugJsonWriter.java?rev=1806158&r1=1806157&r2=1806158&view=diff
==============================================================================
--- felix/trunk/converter/serializer/src/main/java/org/apache/felix/serializer/impl/json/DebugJsonWriter.java (original)
+++ felix/trunk/converter/serializer/src/main/java/org/apache/felix/serializer/impl/json/DebugJsonWriter.java Fri Aug 25 11:22:45 2017
@@ -19,6 +19,8 @@ package org.apache.felix.serializer.impl
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -31,21 +33,23 @@ import org.osgi.util.converter.Converter
public class DebugJsonWriter implements Writer {
- private final Converter converter;
+ private Converter converter;
+ private final Map<String, List<String>> orderingRules;
private final boolean ignoreNull = false;
private final int indentation = 2;
- public DebugJsonWriter(Converter c) {
+ public DebugJsonWriter(Converter c, Map<String,List<String>> rules) {
converter = c;
+ orderingRules = rules;
}
@Override
public String write(Object obj) {
- return encode(obj, 0).trim();
+ return encode(obj, "/", 0).trim();
}
@SuppressWarnings("rawtypes")
- private String encode(Object obj, int level) {
+ private String encode(Object obj, String path, int level) {
if (obj == null) {
return ignoreNull ? "" : "null";
}
@@ -53,13 +57,14 @@ public class DebugJsonWriter implements
if (obj instanceof String) {
return "\"" + (String)obj + "\"";
} else if (obj instanceof Map) {
- return encodeMap((Map) obj, level);
+ return encodeMap(orderMap((Map)obj, path), path, level);
} else if (obj instanceof Collection) {
- return encodeCollection((Collection) obj, level);
+ return encodeCollection((Collection) obj, path, level);
} else if (obj instanceof DTO) {
- return encodeMap(converter.convert(obj).sourceAsDTO().to(Map.class), level);
+ Map converted = converter.convert(obj).sourceAsDTO().to(Map.class);
+ return encodeMap(orderMap(converted, path), path, level);
} else if (obj.getClass().isArray()) {
- return encodeCollection(asCollection(obj), level);
+ return encodeCollection(asCollection(obj), path, level);
} else if (obj instanceof Number) {
return obj.toString();
} else if (obj instanceof Boolean) {
@@ -69,6 +74,40 @@ public class DebugJsonWriter implements
return "\"" + converter.convert(obj).to(String.class) + "\"";
}
+ @SuppressWarnings( { "unchecked", "rawtypes" } )
+ private Map orderMap(Map unordered, String path) {
+ Map ordered = (orderingRules.containsKey(path)) ? new LinkedHashMap<>() : new TreeMap<>();
+ List<String> keys = (orderingRules.containsKey(path)) ? orderingRules.get(path) : new ArrayList<>(unordered.keySet());
+ for (String key : keys) {
+ String itemPath = (path.endsWith("/")) ? path + key : path + "/" + key;
+ Object value = unordered.get(key);
+ if (value instanceof Map)
+ ordered.put(key, orderMap((Map)value, itemPath));
+ else if(value instanceof Collection)
+ ordered.put(key, orderCollectionItems((Collection)value, itemPath));
+ else
+ ordered.put(key, value);
+ }
+
+ return ordered;
+ }
+
+ @SuppressWarnings( { "unchecked", "rawtypes" } )
+ private List orderCollectionItems(Collection unordered, String path) {
+ List ordered = new ArrayList<>();
+ for (Object obj: unordered) {
+ if (obj instanceof Map)
+ ordered.add(orderMap((Map)obj, path));
+ else if(obj instanceof Collection)
+ ordered.add(orderCollectionItems((Collection)obj, path));
+ else
+ ordered.add(obj);
+ }
+
+ try{Collections.sort(ordered);}catch (Exception e){}
+ return ordered;
+ }
+
private Collection<?> asCollection(Object arr) {
// Arrays.asList() doesn't work for primitive arrays
int len = Array.getLength(arr);
@@ -79,7 +118,7 @@ public class DebugJsonWriter implements
return l;
}
- private String encodeCollection(Collection<?> collection, int level) {
+ private String encodeCollection(Collection<?> collection, String path, int level) {
level++;
StringBuilder sb = new StringBuilder("[\n");
@@ -90,8 +129,8 @@ public class DebugJsonWriter implements
else
sb.append(",\n");
- sb.append( getIdentPrefix(level));
- sb.append(encode(o, level));
+ sb.append(getIdentPrefix(level));
+ sb.append(encode(o, path, level));
}
sb.append("\n");
@@ -101,22 +140,23 @@ public class DebugJsonWriter implements
}
@SuppressWarnings({ "rawtypes", "unchecked" })
- private String encodeMap(Map m, int level) {
+ private String encodeMap(Map m, String path, int level) {
level++;
- Map orderedMap = new TreeMap<>(m);
+// Map orderedMap = (orderingRules.isEmpty()) ? new TreeMap<>(m) : m;
StringBuilder sb = new StringBuilder("{\n");
- for (Entry entry : (Set<Entry>) orderedMap.entrySet()) {
+ for (Entry entry : (Set<Entry>) m.entrySet()) {
if (entry.getKey() == null || entry.getValue() == null)
if (ignoreNull)
continue;
+ String itemPath = (path.endsWith("/")) ? path + entry.getKey() : path + "/" + entry.getKey();
if (sb.length() > 2)
sb.append(",\n");
sb.append(getIdentPrefix(level));
sb.append('"');
sb.append(entry.getKey().toString());
sb.append("\":");
- sb.append(encode(entry.getValue(), level));
+ sb.append(encode(entry.getValue(), itemPath, level));
}
sb.append("\n");
sb.append(getIdentPrefix(--level));
Modified: felix/trunk/converter/serializer/src/main/java/org/apache/felix/serializer/impl/json/DefaultJsonWriter.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/serializer/src/main/java/org/apache/felix/serializer/impl/json/DefaultJsonWriter.java?rev=1806158&r1=1806157&r2=1806158&view=diff
==============================================================================
--- felix/trunk/converter/serializer/src/main/java/org/apache/felix/serializer/impl/json/DefaultJsonWriter.java (original)
+++ felix/trunk/converter/serializer/src/main/java/org/apache/felix/serializer/impl/json/DefaultJsonWriter.java Fri Aug 25 11:22:45 2017
@@ -49,11 +49,6 @@ public class DefaultJsonWriter implement
}
if (obj instanceof String) {
- // Optimization for when the value is already a String
- // David B.: is this ok? Or does the Converter do something else
- // other than just returning a String??
- // I noticed that a lot of calculations were going on, just
- // to return a String anyway.
return "\"" + (String)obj + "\"";
} else if (obj instanceof Map) {
return encodeMap((Map) obj);
Modified: felix/trunk/converter/serializer/src/main/java/org/apache/felix/serializer/impl/json/JsonWriterFactory.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/serializer/src/main/java/org/apache/felix/serializer/impl/json/JsonWriterFactory.java?rev=1806158&r1=1806157&r2=1806158&view=diff
==============================================================================
--- felix/trunk/converter/serializer/src/main/java/org/apache/felix/serializer/impl/json/JsonWriterFactory.java (original)
+++ felix/trunk/converter/serializer/src/main/java/org/apache/felix/serializer/impl/json/JsonWriterFactory.java Fri Aug 25 11:22:45 2017
@@ -16,11 +16,22 @@
*/
package org.apache.felix.serializer.impl.json;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
import org.osgi.service.serializer.Writer;
import org.osgi.service.serializer.WriterFactory;
import org.osgi.util.converter.Converter;
-public class JsonWriterFactory implements WriterFactory {
+public class JsonWriterFactory implements WriterFactory, WriterFactory.JsonWriterFactory {
+ private final Map<String, List<String>> orderingRules = new HashMap<>();
+
+ @Override
+ public JsonWriterFactory orderBy(String path, List<String> keyOrder) {
+ orderingRules.put(path, keyOrder);
+ return this;
+ }
@Override
public Writer newDefaultWriter(Converter c) {
@@ -29,6 +40,6 @@ public class JsonWriterFactory implement
@Override
public Writer newDebugWriter(Converter c) {
- return new DebugJsonWriter(c);
+ return new DebugJsonWriter(c, orderingRules);
}
}
Modified: felix/trunk/converter/serializer/src/main/java/org/apache/felix/serializer/impl/yaml/YamlWriterFactory.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/serializer/src/main/java/org/apache/felix/serializer/impl/yaml/YamlWriterFactory.java?rev=1806158&r1=1806157&r2=1806158&view=diff
==============================================================================
--- felix/trunk/converter/serializer/src/main/java/org/apache/felix/serializer/impl/yaml/YamlWriterFactory.java (original)
+++ felix/trunk/converter/serializer/src/main/java/org/apache/felix/serializer/impl/yaml/YamlWriterFactory.java Fri Aug 25 11:22:45 2017
@@ -16,11 +16,22 @@
*/
package org.apache.felix.serializer.impl.yaml;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
import org.osgi.service.serializer.Writer;
import org.osgi.service.serializer.WriterFactory;
import org.osgi.util.converter.Converter;
-public class YamlWriterFactory implements WriterFactory {
+public class YamlWriterFactory implements WriterFactory, WriterFactory.YamlWriterFactory {
+ private final Map<String, List<String>> orderingRules = new HashMap<>();
+
+ @Override
+ public YamlWriterFactory orderBy(String path, List<String> keyOrder) {
+ orderingRules.put(path,keyOrder);
+ return this;
+ }
@Override
public Writer newDefaultWriter(Converter c) {
Modified: felix/trunk/converter/serializer/src/main/java/org/osgi/service/serializer/WriterFactory.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/serializer/src/main/java/org/osgi/service/serializer/WriterFactory.java?rev=1806158&r1=1806157&r2=1806158&view=diff
==============================================================================
--- felix/trunk/converter/serializer/src/main/java/org/osgi/service/serializer/WriterFactory.java (original)
+++ felix/trunk/converter/serializer/src/main/java/org/osgi/service/serializer/WriterFactory.java Fri Aug 25 11:22:45 2017
@@ -15,6 +15,8 @@
*/
package org.osgi.service.serializer;
+import java.util.List;
+
import org.osgi.annotation.versioning.ProviderType;
import org.osgi.util.converter.Converter;
@@ -44,6 +46,22 @@ public interface WriterFactory {
Writer newDebugWriter(Converter c);
/**
+ * Register an ordering rule for this writer.
+ *
+ * An ordering rule causes the written json to be output in the order
+ * specified. This can be useful, for example, for debugging or when
+ * the data otherwise needs to be human consumable.
+ *
+ * Note that only the target type is specified, so the rule will be visited
+ * for every conversion to the target type.
+ *
+ * @param path the path where the key is located in the object graph.
+ * @param func The desired key order.
+ * @return This factory object to allow further invocations on it.
+ */
+ WriterFactory orderBy(String path, List<String> keyOrder);
+
+ /**
* A convenience means of obtaining a JsonWriterFactory without having to
* configure service settings.
*/
Modified: felix/trunk/converter/serializer/src/test/java/org/apache/felix/serializer/impl/json/JsonBackingObjectSerializationTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/converter/serializer/src/test/java/org/apache/felix/serializer/impl/json/JsonBackingObjectSerializationTest.java?rev=1806158&r1=1806157&r2=1806158&view=diff
==============================================================================
--- felix/trunk/converter/serializer/src/test/java/org/apache/felix/serializer/impl/json/JsonBackingObjectSerializationTest.java (original)
+++ felix/trunk/converter/serializer/src/test/java/org/apache/felix/serializer/impl/json/JsonBackingObjectSerializationTest.java Fri Aug 25 11:22:45 2017
@@ -17,6 +17,7 @@
package org.apache.felix.serializer.impl.json;
import java.lang.reflect.Type;
+import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@@ -74,6 +75,19 @@ public class JsonBackingObjectSerializat
assertEquals(EXPECTED, actual);
}
+ @Test
+ public void testOrderedSerialization() {
+ final JsonWriterFactory factory = new JsonWriterFactory();
+ factory.orderBy("/", Arrays.asList("b", "a", "o", "l2", "l1"));
+ factory.orderBy("/l2", Arrays.asList("b", "a"));
+ final String actual = new JsonSerializerImpl()
+ .serialize(MyDTOishObject.factory("A", "B"))
+ .writeWith(factory.newDebugWriter(Converters.standardConverter()))
+ .toString();
+
+ assertEquals(ORDERED, actual);
+ }
+
public static class MyDTOishObject extends DTO {
public String a;
public String b;
@@ -138,10 +152,10 @@ public class JsonBackingObjectSerializat
" \"a\":\"A\",\n" +
" \"b\":\"B\",\n" +
" \"l1\":[\n" +
+ " \"four\",\n" +
" \"one\",\n" +
- " \"two\",\n" +
" \"three\",\n" +
- " \"four\"\n" +
+ " \"two\"\n" +
" ],\n" +
" \"l2\":[\n" +
" {\n" +
@@ -158,4 +172,30 @@ public class JsonBackingObjectSerializat
" \"b\":\"BB\"\n" +
" }\n" +
"}";
+
+ private static final String ORDERED =
+ "{\n" +
+ " \"b\":\"B\",\n" +
+ " \"a\":\"A\",\n" +
+ " \"o\":{\n" +
+ " \"a\":\"AA\",\n" +
+ " \"b\":\"BB\"\n" +
+ " },\n" +
+ " \"l2\":[\n" +
+ " {\n" +
+ " \"b\":\"B\",\n" +
+ " \"a\":\"A\"\n" +
+ " },\n" +
+ " {\n" +
+ " \"b\":\"BB\",\n" +
+ " \"a\":\"AA\"\n" +
+ " }\n" +
+ " ],\n" +
+ " \"l1\":[\n" +
+ " \"four\",\n" +
+ " \"one\",\n" +
+ " \"three\",\n" +
+ " \"two\"\n" +
+ " ]\n" +
+ "}";
}