ONOS-743 - Use REST API Codecs to generate JSON for CLI commands

Change-Id: I25e0840d1af03341c638f837498c95275e6cf31b
diff --git a/cli/pom.xml b/cli/pom.xml
index 1ed1d47..2a9e922 100644
--- a/cli/pom.xml
+++ b/cli/pom.xml
@@ -41,6 +41,10 @@
             <groupId>org.onosproject</groupId>
             <artifactId>onlab-osgi</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-core-common</artifactId>
+        </dependency>
 
         <dependency>
             <groupId>com.fasterxml.jackson.core</groupId>
diff --git a/cli/src/main/java/org/onosproject/cli/AbstractShellCommand.java b/cli/src/main/java/org/onosproject/cli/AbstractShellCommand.java
index 4dd767b..8945d62 100644
--- a/cli/src/main/java/org/onosproject/cli/AbstractShellCommand.java
+++ b/cli/src/main/java/org/onosproject/cli/AbstractShellCommand.java
@@ -15,20 +15,24 @@
  */
 package org.onosproject.cli;
 
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.node.ObjectNode;
 import org.apache.karaf.shell.commands.Option;
 import org.apache.karaf.shell.console.AbstractAction;
 import org.onlab.osgi.DefaultServiceDirectory;
 import org.onlab.osgi.ServiceNotFoundException;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.CodecService;
+import org.onosproject.codec.JsonCodec;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
 import org.onosproject.net.Annotations;
 
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
 /**
  * Base abstraction of Karaf shell commands.
  */
-public abstract class AbstractShellCommand extends AbstractAction {
+public abstract class AbstractShellCommand extends AbstractAction implements CodecContext {
 
     @Option(name = "-j", aliases = "--json", description = "Output JSON",
             required = false, multiValued = false)
@@ -129,4 +133,35 @@
         return null;
     }
 
+
+
+    private final ObjectMapper mapper = new ObjectMapper();
+
+    @Override
+    public ObjectMapper mapper() {
+        return mapper;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public <T> JsonCodec<T> codec(Class<T> entityClass) {
+        return get(CodecService.class).getCodec(entityClass);
+    }
+
+    @Override
+    public <T> T getService(Class<T> serviceClass) {
+        return get(serviceClass);
+    }
+
+    /**
+     * Generates a Json representation of an object.
+     *
+     * @param entity object to generate JSON for
+     * @param entityClass class to format with - this chooses which codec to use
+     * @param <T> Type of the object being formatted
+     * @return JSON object representation
+     */
+    public <T> ObjectNode jsonForEntity(T entity, Class<T> entityClass) {
+        return codec(entityClass).encode(entity, this);
+    }
 }
diff --git a/cli/src/main/java/org/onosproject/cli/app/ApplicationsListCommand.java b/cli/src/main/java/org/onosproject/cli/app/ApplicationsListCommand.java
index c518b43..17cf89e 100644
--- a/cli/src/main/java/org/onosproject/cli/app/ApplicationsListCommand.java
+++ b/cli/src/main/java/org/onosproject/cli/app/ApplicationsListCommand.java
@@ -15,9 +15,9 @@
  */
 package org.onosproject.cli.app;
 
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.node.ArrayNode;
+import java.util.Collections;
+import java.util.List;
+
 import org.apache.karaf.shell.commands.Command;
 import org.apache.karaf.shell.commands.Option;
 import org.onosproject.app.ApplicationService;
@@ -25,8 +25,9 @@
 import org.onosproject.cli.Comparators;
 import org.onosproject.core.Application;
 
-import java.util.Collections;
-import java.util.List;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
 
 import static com.google.common.collect.Lists.newArrayList;
 import static org.onosproject.app.ApplicationState.ACTIVE;
@@ -88,25 +89,12 @@
         for (Application app : apps) {
             boolean isActive = service.getState(app.id()) == ACTIVE;
             if (activeOnly && isActive || !activeOnly) {
-                result.add(json(service, mapper, app));
+                result.add(jsonForEntity(app, Application.class));
             }
         }
         return result;
     }
 
-    protected JsonNode json(ApplicationService service, ObjectMapper mapper,
-                            Application app) {
-        return mapper.createObjectNode()
-                .put("name", app.id().name())
-                .put("id", app.id().id())
-                .put("version", app.version().toString())
-                .put("description", app.description())
-                .put("origin", app.origin())
-                .put("permissions", app.permissions().toString())
-                .put("featuresRepo", app.featuresRepo().isPresent() ?
-                        app.featuresRepo().get().toString() : "")
-                .put("features", app.features().toString())
-                .put("state", service.getState(app.id()).toString());
-    }
+
 
 }
diff --git a/cli/src/main/java/org/onosproject/cli/net/ClusterLinksCommand.java b/cli/src/main/java/org/onosproject/cli/net/ClusterLinksCommand.java
index fb0c941..dc63bff 100644
--- a/cli/src/main/java/org/onosproject/cli/net/ClusterLinksCommand.java
+++ b/cli/src/main/java/org/onosproject/cli/net/ClusterLinksCommand.java
@@ -43,7 +43,7 @@
         if (cluster == null) {
             error("No such cluster %s", cid);
         } else if (outputJson()) {
-            print("%s", json(service.getClusterLinks(topology, cluster)));
+            print("%s", json(this, service.getClusterLinks(topology, cluster)));
         } else {
             for (Link link : service.getClusterLinks(topology, cluster)) {
                 print(linkString(link));
diff --git a/cli/src/main/java/org/onosproject/cli/net/ClustersListCommand.java b/cli/src/main/java/org/onosproject/cli/net/ClustersListCommand.java
index 0c2d2f4..21c2732 100644
--- a/cli/src/main/java/org/onosproject/cli/net/ClustersListCommand.java
+++ b/cli/src/main/java/org/onosproject/cli/net/ClustersListCommand.java
@@ -55,12 +55,11 @@
     private JsonNode json(Iterable<TopologyCluster> clusters) {
         ObjectMapper mapper = new ObjectMapper();
         ArrayNode result = mapper.createArrayNode();
-        for (TopologyCluster cluster : clusters) {
-            result.add(mapper.createObjectNode()
-                               .put("id", cluster.id().index())
-                               .put("deviceCount", cluster.deviceCount())
-                               .put("linkCount", cluster.linkCount()));
-        }
+
+        clusters.spliterator()
+                .forEachRemaining(cluster ->
+                        result.add(jsonForEntity(cluster, TopologyCluster.class)));
+
         return result;
     }
 
diff --git a/cli/src/main/java/org/onosproject/cli/net/DevicePortsListCommand.java b/cli/src/main/java/org/onosproject/cli/net/DevicePortsListCommand.java
index 3e2510c..494273c 100644
--- a/cli/src/main/java/org/onosproject/cli/net/DevicePortsListCommand.java
+++ b/cli/src/main/java/org/onosproject/cli/net/DevicePortsListCommand.java
@@ -116,7 +116,7 @@
                                   .set("annotations", annotations(mapper, port.annotations())));
             }
         }
-        result.set("device", json(service, mapper, device));
+        result.set("device", jsonForEntity(device, Device.class));
         result.set("ports", ports);
         return result;
     }
diff --git a/cli/src/main/java/org/onosproject/cli/net/DevicesListCommand.java b/cli/src/main/java/org/onosproject/cli/net/DevicesListCommand.java
index 852e5f5..e40bcad 100644
--- a/cli/src/main/java/org/onosproject/cli/net/DevicesListCommand.java
+++ b/cli/src/main/java/org/onosproject/cli/net/DevicesListCommand.java
@@ -15,18 +15,18 @@
  */
 package org.onosproject.cli.net;
 
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.node.ArrayNode;
-import com.fasterxml.jackson.databind.node.ObjectNode;
+import java.util.Collections;
+import java.util.List;
+
 import org.apache.karaf.shell.commands.Command;
 import org.onosproject.cli.AbstractShellCommand;
 import org.onosproject.cli.Comparators;
 import org.onosproject.net.Device;
 import org.onosproject.net.device.DeviceService;
 
-import java.util.Collections;
-import java.util.List;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
 
 import static com.google.common.collect.Lists.newArrayList;
 
@@ -44,7 +44,7 @@
     protected void execute() {
         DeviceService service = get(DeviceService.class);
         if (outputJson()) {
-            print("%s", json(service, getSortedDevices(service)));
+            print("%s", json(getSortedDevices(service)));
         } else {
             for (Device device : getSortedDevices(service)) {
                 printDevice(service, device);
@@ -55,40 +55,14 @@
     /**
      * Returns JSON node representing the specified devices.
      *
-     * @param service device service
      * @param devices collection of devices
      * @return JSON node
      */
-    public static JsonNode json(DeviceService service, Iterable<Device> devices) {
+    private JsonNode json(Iterable<Device> devices) {
         ObjectMapper mapper = new ObjectMapper();
         ArrayNode result = mapper.createArrayNode();
         for (Device device : devices) {
-            result.add(json(service, mapper, device));
-        }
-        return result;
-    }
-
-    /**
-     * Returns JSON node representing the specified device.
-     *
-     * @param service device service
-     * @param mapper  object mapper
-     * @param device  infrastructure device
-     * @return JSON node
-     */
-    public static ObjectNode json(DeviceService service, ObjectMapper mapper,
-                                  Device device) {
-        ObjectNode result = mapper.createObjectNode();
-        if (device != null) {
-            result.put("id", device.id().toString())
-                    .put("available", service.isAvailable(device.id()))
-                    .put("type", device.type().toString())
-                    .put("role", service.getRole(device.id()).toString())
-                    .put("mfr", device.manufacturer())
-                    .put("hw", device.hwVersion())
-                    .put("sw", device.swVersion())
-                    .put("serial", device.serialNumber())
-                    .set("annotations", annotations(mapper, device.annotations()));
+            result.add(jsonForEntity(device, Device.class));
         }
         return result;
     }
diff --git a/cli/src/main/java/org/onosproject/cli/net/FlowsListCommand.java b/cli/src/main/java/org/onosproject/cli/net/FlowsListCommand.java
index dabeab9..f123df6 100644
--- a/cli/src/main/java/org/onosproject/cli/net/FlowsListCommand.java
+++ b/cli/src/main/java/org/onosproject/cli/net/FlowsListCommand.java
@@ -15,31 +15,28 @@
  */
 package org.onosproject.cli.net;
 
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.node.ArrayNode;
-import com.fasterxml.jackson.databind.node.ObjectNode;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
 
 import org.apache.karaf.shell.commands.Argument;
 import org.apache.karaf.shell.commands.Command;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.core.CoreService;
 import org.onosproject.cli.AbstractShellCommand;
 import org.onosproject.cli.Comparators;
+import org.onosproject.core.CoreService;
 import org.onosproject.net.Device;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.flow.FlowEntry;
 import org.onosproject.net.flow.FlowEntry.FlowEntryState;
 import org.onosproject.net.flow.FlowRuleService;
-import org.onosproject.net.flow.criteria.Criterion;
-import org.onosproject.net.flow.instructions.Instruction;
 
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.SortedMap;
-import java.util.TreeMap;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
 
 import static com.google.common.collect.Lists.newArrayList;
 
@@ -73,7 +70,7 @@
         SortedMap<Device, List<FlowEntry>> flows = getSortedFlows(deviceService, service);
 
         if (outputJson()) {
-            print("%s", json(coreService, flows.keySet(), flows));
+            print("%s", json(flows.keySet(), flows));
         } else {
             flows.forEach((device, flow) -> printFlows(device, flow, coreService));
         }
@@ -82,30 +79,27 @@
     /**
      * Produces a JSON array of flows grouped by the each device.
      *
-     * @param coreService core service
      * @param devices     collection of devices to group flow by
      * @param flows       collection of flows per each device
      * @return JSON array
      */
-    private JsonNode json(CoreService coreService, Iterable<Device> devices,
+    private JsonNode json(Iterable<Device> devices,
                           Map<Device, List<FlowEntry>> flows) {
         ObjectMapper mapper = new ObjectMapper();
         ArrayNode result = mapper.createArrayNode();
         for (Device device : devices) {
-            result.add(json(coreService, mapper, device, flows.get(device)));
+            result.add(json(mapper, device, flows.get(device)));
         }
         return result;
     }
 
     // Produces JSON object with the flows of the given device.
-    private ObjectNode json(CoreService coreService, ObjectMapper mapper,
+    private ObjectNode json(ObjectMapper mapper,
                             Device device, List<FlowEntry> flows) {
         ObjectNode result = mapper.createObjectNode();
         ArrayNode array = mapper.createArrayNode();
 
-        for (FlowEntry flow : flows) {
-            array.add(json(coreService, mapper, flow));
-        }
+        flows.forEach(flow -> array.add(jsonForEntity(flow, FlowEntry.class)));
 
         result.put("device", device.id().toString())
                 .put("flowCount", flows.size())
@@ -113,37 +107,6 @@
         return result;
     }
 
-    // Produces JSON structure with the specified flow data.
-    private ObjectNode json(CoreService coreService, ObjectMapper mapper,
-                            FlowEntry flow) {
-        ObjectNode result = mapper.createObjectNode();
-        ArrayNode crit = mapper.createArrayNode();
-        for (Criterion c : flow.selector().criteria()) {
-            crit.add(c.toString());
-        }
-
-        ArrayNode instr = mapper.createArrayNode();
-        for (Instruction i : flow.treatment().allInstructions()) {
-            instr.add(i.toString());
-        }
-
-        ApplicationId appCoreId = coreService.getAppId(flow.appId());
-        String appName = appCoreId == null ?
-                Short.toString(flow.appId())
-                : appCoreId.name();
-
-        result.put("flowId", Long.toHexString(flow.id().value()))
-                .put("state", flow.state().toString())
-                .put("bytes", flow.bytes())
-                .put("packets", flow.packets())
-                .put("life", flow.life())
-                .put("tableId", flow.tableId())
-                .put("appId", appName);
-        result.set("selector", crit);
-        result.set("treatment", instr);
-        return result;
-    }
-
     /**
      * Returns the list of devices sorted using the device ID URIs.
      *
diff --git a/cli/src/main/java/org/onosproject/cli/net/HostsListCommand.java b/cli/src/main/java/org/onosproject/cli/net/HostsListCommand.java
index 10d015f..a90627e 100644
--- a/cli/src/main/java/org/onosproject/cli/net/HostsListCommand.java
+++ b/cli/src/main/java/org/onosproject/cli/net/HostsListCommand.java
@@ -15,19 +15,18 @@
  */
 package org.onosproject.cli.net;
 
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.node.ArrayNode;
-import com.fasterxml.jackson.databind.node.ObjectNode;
+import java.util.Collections;
+import java.util.List;
+
 import org.apache.karaf.shell.commands.Command;
 import org.onosproject.cli.AbstractShellCommand;
 import org.onosproject.cli.Comparators;
 import org.onosproject.net.Host;
 import org.onosproject.net.host.HostService;
-import org.onlab.packet.IpAddress;
 
-import java.util.Collections;
-import java.util.List;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
 
 import static com.google.common.collect.Lists.newArrayList;
 
@@ -54,30 +53,11 @@
     }
 
     // Produces JSON structure.
-    private static JsonNode json(Iterable<Host> hosts) {
+    private JsonNode json(Iterable<Host> hosts) {
         ObjectMapper mapper = new ObjectMapper();
         ArrayNode result = mapper.createArrayNode();
-        for (Host host : hosts) {
-            result.add(json(mapper, host));
-        }
-        return result;
-    }
 
-    // Produces JSON structure.
-    private static JsonNode json(ObjectMapper mapper, Host host) {
-        ObjectNode loc = LinksListCommand.json(mapper, host.location())
-                .put("time", host.location().time());
-        ArrayNode ips = mapper.createArrayNode();
-        for (IpAddress ip : host.ipAddresses()) {
-            ips.add(ip.toString());
-        }
-        ObjectNode result = mapper.createObjectNode()
-                .put("id", host.id().toString())
-                .put("mac", host.mac().toString())
-                .put("vlan", host.vlan().toString());
-        result.set("location", loc);
-        result.set("ips", ips);
-        result.set("annotations", annotations(mapper, host.annotations()));
+        hosts.forEach(host -> result.add(jsonForEntity(host, Host.class)));
         return result;
     }
 
diff --git a/cli/src/main/java/org/onosproject/cli/net/IntentsListCommand.java b/cli/src/main/java/org/onosproject/cli/net/IntentsListCommand.java
index a52b7e3..55b9ec9 100644
--- a/cli/src/main/java/org/onosproject/cli/net/IntentsListCommand.java
+++ b/cli/src/main/java/org/onosproject/cli/net/IntentsListCommand.java
@@ -15,16 +15,11 @@
  */
 package org.onosproject.cli.net;
 
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.node.ArrayNode;
-import com.fasterxml.jackson.databind.node.ObjectNode;
+import java.util.List;
+
 import org.apache.karaf.shell.commands.Command;
 import org.apache.karaf.shell.commands.Option;
 import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.Link;
-import org.onosproject.net.NetworkResource;
 import org.onosproject.net.intent.ConnectivityIntent;
 import org.onosproject.net.intent.HostToHostIntent;
 import org.onosproject.net.intent.Intent;
@@ -36,8 +31,10 @@
 import org.onosproject.net.intent.PointToPointIntent;
 import org.onosproject.net.intent.SinglePointToMultiPointIntent;
 
-import java.util.List;
-import java.util.Set;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
 
 /**
  * Lists the inventory of intents and their states.
@@ -393,81 +390,9 @@
     private JsonNode json(IntentService service, Iterable<Intent> intents) {
         ObjectMapper mapper = new ObjectMapper();
         ArrayNode result = mapper.createArrayNode();
-        for (Intent intent : intents) {
-            result.add(json(service, mapper, intent));
-        }
+
+        intents.forEach(intent -> result.add(jsonForEntity(intent, Intent.class)));
         return result;
     }
 
-    private JsonNode json(IntentService service, ObjectMapper mapper, Intent intent) {
-        ObjectNode result = mapper.createObjectNode()
-                .put("id", intent.id().toString())
-                .put("type", intent.getClass().getSimpleName())
-                .put("appId", intent.appId().name());
-
-        IntentState state = service.getIntentState(intent.key());
-        if (state != null) {
-            result.put("state", state.toString());
-        }
-
-        if (!intent.resources().isEmpty()) {
-            ArrayNode rnode = mapper.createArrayNode();
-            for (NetworkResource resource : intent.resources()) {
-                rnode.add(resource.toString());
-            }
-            result.set("resources", rnode);
-        }
-
-        if (intent instanceof ConnectivityIntent) {
-            ConnectivityIntent ci = (ConnectivityIntent) intent;
-            if (!ci.selector().criteria().isEmpty()) {
-                result.put("selector", ci.selector().criteria().toString());
-            }
-            if (!ci.treatment().allInstructions().isEmpty()) {
-                result.put("treatment", ci.treatment().allInstructions().toString());
-            }
-        }
-
-        if (intent instanceof PathIntent) {
-            PathIntent pi = (PathIntent) intent;
-            ArrayNode pnode = mapper.createArrayNode();
-            for (Link link : pi.path().links()) {
-                pnode.add(link.toString());
-            }
-            result.set("path", pnode);
-        } else if (intent instanceof HostToHostIntent) {
-            HostToHostIntent pi = (HostToHostIntent) intent;
-            result.set("host1", LinksListCommand.json(mapper, pi.one()));
-            result.set("host2", LinksListCommand.json(mapper, pi.two()));
-        } else if (intent instanceof PointToPointIntent) {
-            PointToPointIntent pi = (PointToPointIntent) intent;
-            result.set("ingress", LinksListCommand.json(mapper, pi.ingressPoint()));
-            result.set("egress", LinksListCommand.json(mapper, pi.egressPoint()));
-        } else if (intent instanceof MultiPointToSinglePointIntent) {
-            MultiPointToSinglePointIntent pi = (MultiPointToSinglePointIntent) intent;
-            result.set("ingress", json(mapper, pi.ingressPoints()));
-            result.set("egress", LinksListCommand.json(mapper, pi.egressPoint()));
-        } else if (intent instanceof SinglePointToMultiPointIntent) {
-            SinglePointToMultiPointIntent pi = (SinglePointToMultiPointIntent) intent;
-            result.set("ingress", LinksListCommand.json(mapper, pi.ingressPoint()));
-            result.set("egress", json(mapper, pi.egressPoints()));
-        } else if (intent instanceof LinkCollectionIntent) {
-            LinkCollectionIntent li = (LinkCollectionIntent) intent;
-            result.set("links", LinksListCommand.json(li.links()));
-        }
-
-        List<Intent> installable = service.getInstallableIntents(intent.key());
-        if (installable != null && !installable.isEmpty()) {
-            result.set("installable", json(service, installable));
-        }
-        return result;
-    }
-
-    private JsonNode json(ObjectMapper mapper, Set<ConnectPoint> connectPoints) {
-        ArrayNode result = mapper.createArrayNode();
-        for (ConnectPoint cp : connectPoints) {
-            result.add(LinksListCommand.json(mapper, cp));
-        }
-        return result;
-    }
 }
diff --git a/cli/src/main/java/org/onosproject/cli/net/LinksListCommand.java b/cli/src/main/java/org/onosproject/cli/net/LinksListCommand.java
index 99872ac..b9403a3 100644
--- a/cli/src/main/java/org/onosproject/cli/net/LinksListCommand.java
+++ b/cli/src/main/java/org/onosproject/cli/net/LinksListCommand.java
@@ -15,17 +15,16 @@
  */
 package org.onosproject.cli.net;
 
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.Link;
+import org.onosproject.net.link.LinkService;
+
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
-import org.apache.karaf.shell.commands.Argument;
-import org.apache.karaf.shell.commands.Command;
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.HostId;
-import org.onosproject.net.Link;
-import org.onosproject.net.link.LinkService;
 
 import static org.onosproject.net.DeviceId.deviceId;
 
@@ -49,7 +48,7 @@
         Iterable<Link> links = uri != null ?
                 service.getDeviceLinks(deviceId(uri)) : service.getLinks();
         if (outputJson()) {
-            print("%s", json(links));
+            print("%s", json(this, links));
         } else {
             for (Link link : links) {
                 print(linkString(link));
@@ -60,59 +59,28 @@
     /**
      * Produces a JSON array containing the specified links.
      *
+     * @param context context to use for looking up codecs
      * @param links collection of links
      * @return JSON array
      */
-    public static JsonNode json(Iterable<Link> links) {
+    public static JsonNode json(AbstractShellCommand context, Iterable<Link> links) {
         ObjectMapper mapper = new ObjectMapper();
         ArrayNode result = mapper.createArrayNode();
-        for (Link link : links) {
-            result.add(json(mapper, link));
-        }
+
+        links.forEach(link -> result.add(context.jsonForEntity(link, Link.class)));
+
         return result;
     }
 
     /**
      * Produces a JSON object for the specified link.
      *
-     * @param mapper object mapper
+     * @param context context to use for looking up codecs
      * @param link   link to encode
      * @return JSON object
      */
-    public static ObjectNode json(ObjectMapper mapper, Link link) {
-        ObjectNode result = mapper.createObjectNode();
-        result.set("src", json(mapper, link.src()));
-        result.set("dst", json(mapper, link.dst()));
-        result.put("type", link.type().toString());
-        result.put("state", link.state().toString());
-        result.set("annotations", annotations(mapper, link.annotations()));
-        return result;
-    }
-
-    /**
-     * Produces a JSON object for the specified host ID.
-     *
-     * @param mapper    object mapper
-     * @param hostId    host ID to encode
-     * @return JSON object
-     */
-    public static ObjectNode json(ObjectMapper mapper, HostId hostId) {
-        return mapper.createObjectNode()
-                .put("mac", hostId.mac().toString())
-                .put("vlanId", hostId.vlanId().toString());
-    }
-
-    /**
-     * Produces a JSON object for the specified connect point.
-     *
-     * @param mapper       object mapper
-     * @param connectPoint connection point to encode
-     * @return JSON object
-     */
-    public static ObjectNode json(ObjectMapper mapper, ConnectPoint connectPoint) {
-        return mapper.createObjectNode()
-                .put("device", connectPoint.deviceId().toString())
-                .put("port", connectPoint.port().toString());
+    public static ObjectNode json(AbstractShellCommand context, Link link) {
+         return context.jsonForEntity(link, Link.class);
     }
 
     /**
diff --git a/cli/src/main/java/org/onosproject/cli/net/PathListCommand.java b/cli/src/main/java/org/onosproject/cli/net/PathListCommand.java
index a76a878..141ae26 100644
--- a/cli/src/main/java/org/onosproject/cli/net/PathListCommand.java
+++ b/cli/src/main/java/org/onosproject/cli/net/PathListCommand.java
@@ -20,6 +20,7 @@
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import org.apache.karaf.shell.commands.Argument;
 import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
 import org.onosproject.net.Link;
 import org.onosproject.net.Path;
 
@@ -51,7 +52,7 @@
         init();
         Set<Path> paths = service.getPaths(topology, deviceId(src), deviceId(dst));
         if (outputJson()) {
-            print("%s", json(paths));
+            print("%s", json(this, paths));
         } else {
             for (Path path : paths) {
                 print(pathString(path));
@@ -62,16 +63,17 @@
     /**
      * Produces a JSON array containing the specified paths.
      *
+     * @param context context to use for looking up codecs
      * @param paths collection of paths
      * @return JSON array
      */
-    public static JsonNode json(Iterable<Path> paths) {
+    public static JsonNode json(AbstractShellCommand context, Iterable<Path> paths) {
         ObjectMapper mapper = new ObjectMapper();
         ArrayNode result = mapper.createArrayNode();
         for (Path path : paths) {
-            result.add(LinksListCommand.json(mapper, path)
-                               .put("cost", path.cost())
-                               .set("links", LinksListCommand.json(path.links())));
+            result.add(LinksListCommand.json(context, path)
+                    .put("cost", path.cost())
+                    .set("links", LinksListCommand.json(context, path.links())));
         }
         return result;
     }
diff --git a/cli/src/main/java/org/onosproject/cli/net/TopologyCommand.java b/cli/src/main/java/org/onosproject/cli/net/TopologyCommand.java
index b454645..e1ebd25 100644
--- a/cli/src/main/java/org/onosproject/cli/net/TopologyCommand.java
+++ b/cli/src/main/java/org/onosproject/cli/net/TopologyCommand.java
@@ -27,8 +27,6 @@
 import org.onosproject.net.topology.TopologyProvider;
 import org.onosproject.net.topology.TopologyService;
 
-import com.fasterxml.jackson.databind.ObjectMapper;
-
 /**
  * Lists summary of the current topology.
  */
@@ -64,14 +62,7 @@
 
         } else if (outputJson()) {
             print("%s",
-                    new ObjectMapper()
-                            .createObjectNode()
-                            .put("time", topology.time())
-                            .put("created", formatCreationTime(topology.creationTime()))
-                            .put("uptime", formatElapsedTime(topologyUptime))
-                            .put("deviceCount", topology.deviceCount())
-                            .put("linkCount", topology.linkCount())
-                            .put("clusterCount", topology.clusterCount()));
+                    jsonForEntity(topology, Topology.class));
         } else {
             print(FMT, formatCreationTime(topology.creationTime()),
                     formatElapsedTime(topologyUptime),