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" +
+            "}";
 }