Adding JSON format to the CLI. Intents and flows still need to be done.
diff --git a/cli/pom.xml b/cli/pom.xml
index cc1d9dd..8a11f34 100644
--- a/cli/pom.xml
+++ b/cli/pom.xml
@@ -26,6 +26,16 @@
             <groupId>org.onlab.onos</groupId>
             <artifactId>onlab-osgi</artifactId>
         </dependency>
+
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-annotations</artifactId>
+        </dependency>
+
         <dependency>
             <groupId>org.osgi</groupId>
             <artifactId>org.osgi.core</artifactId>
diff --git a/cli/src/main/java/org/onlab/onos/cli/AbstractShellCommand.java b/cli/src/main/java/org/onlab/onos/cli/AbstractShellCommand.java
index 184a7e6..839d2841 100644
--- a/cli/src/main/java/org/onlab/onos/cli/AbstractShellCommand.java
+++ b/cli/src/main/java/org/onlab/onos/cli/AbstractShellCommand.java
@@ -1,5 +1,6 @@
 package org.onlab.onos.cli;
 
+import org.apache.karaf.shell.commands.Option;
 import org.apache.karaf.shell.console.OsgiCommandSupport;
 import org.onlab.osgi.DefaultServiceDirectory;
 import org.onlab.osgi.ServiceNotFoundException;
@@ -9,6 +10,10 @@
  */
 public abstract class AbstractShellCommand extends OsgiCommandSupport {
 
+    @Option(name = "-j", aliases = "--json", description = "Output JSON",
+            required = false, multiValued = false)
+    private boolean json = false;
+
     /**
      * Returns the reference to the implementation of the specified service.
      *
@@ -46,6 +51,15 @@
      */
     protected abstract void execute();
 
+    /**
+     * Indicates whether JSON format should be output.
+     *
+     * @return true if JSON is requested
+     */
+    protected boolean outputJson() {
+        return json;
+    }
+
     @Override
     protected Object doExecute() throws Exception {
         try {
diff --git a/cli/src/main/java/org/onlab/onos/cli/MastersListCommand.java b/cli/src/main/java/org/onlab/onos/cli/MastersListCommand.java
index 0f7857f..fff4955 100644
--- a/cli/src/main/java/org/onlab/onos/cli/MastersListCommand.java
+++ b/cli/src/main/java/org/onlab/onos/cli/MastersListCommand.java
@@ -1,7 +1,9 @@
 package org.onlab.onos.cli;
 
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.google.common.collect.Lists;
-
 import org.apache.karaf.shell.commands.Command;
 import org.onlab.onos.cluster.ClusterService;
 import org.onlab.onos.cluster.ControllerNode;
@@ -26,15 +28,50 @@
         MastershipService mastershipService = get(MastershipService.class);
         List<ControllerNode> nodes = newArrayList(service.getNodes());
         Collections.sort(nodes, Comparators.NODE_COMPARATOR);
+
+        if (outputJson()) {
+            print("%s", json(service, mastershipService, nodes));
+        } else {
+            for (ControllerNode node : nodes) {
+                List<DeviceId> ids = Lists.newArrayList(mastershipService.getDevicesOf(node.id()));
+                Collections.sort(ids, Comparators.ELEMENT_ID_COMPARATOR);
+                print("%s: %d devices", node.id(), ids.size());
+                for (DeviceId deviceId : ids) {
+                    print("  %s", deviceId);
+                }
+            }
+        }
+    }
+
+    // Produces JSON structure.
+    private JsonNode json(ClusterService service, MastershipService mastershipService,
+                          List<ControllerNode> nodes) {
+        ObjectMapper mapper = new ObjectMapper();
+        ArrayNode result = mapper.createArrayNode();
         ControllerNode self = service.getLocalNode();
         for (ControllerNode node : nodes) {
             List<DeviceId> ids = Lists.newArrayList(mastershipService.getDevicesOf(node.id()));
-            Collections.sort(ids, Comparators.ELEMENT_ID_COMPARATOR);
-            print("%s: %d devices", node.id(), ids.size());
-            for (DeviceId deviceId : ids) {
-                print("  %s", deviceId);
-            }
+            result.add(mapper.createObjectNode()
+                               .put("id", node.id().toString())
+                               .put("size", ids.size())
+                               .set("devices", json(mapper, ids)));
         }
+        return result;
+    }
+
+    /**
+     * Produces a JSON array containing the specified device identifiers.
+     *
+     * @param mapper object mapper
+     * @param ids    collection of device identifiers
+     * @return JSON array
+     */
+    public static JsonNode json(ObjectMapper mapper, Iterable<DeviceId> ids) {
+        ArrayNode result = mapper.createArrayNode();
+        for (DeviceId deviceId : ids) {
+            result.add(deviceId.toString());
+        }
+        return result;
     }
 
 }
diff --git a/cli/src/main/java/org/onlab/onos/cli/NodesListCommand.java b/cli/src/main/java/org/onlab/onos/cli/NodesListCommand.java
index b7b4556..d9bdf94 100644
--- a/cli/src/main/java/org/onlab/onos/cli/NodesListCommand.java
+++ b/cli/src/main/java/org/onlab/onos/cli/NodesListCommand.java
@@ -1,5 +1,8 @@
 package org.onlab.onos.cli;
 
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
 import org.apache.karaf.shell.commands.Command;
 import org.onlab.onos.cluster.ClusterService;
 import org.onlab.onos.cluster.ControllerNode;
@@ -24,12 +27,32 @@
         ClusterService service = get(ClusterService.class);
         List<ControllerNode> nodes = newArrayList(service.getNodes());
         Collections.sort(nodes, Comparators.NODE_COMPARATOR);
+        if (outputJson()) {
+            print("%s", json(service, nodes));
+        } else {
+            ControllerNode self = service.getLocalNode();
+            for (ControllerNode node : nodes) {
+                print(FMT, node.id(), node.ip(), node.tcpPort(),
+                      service.getState(node.id()),
+                      node.equals(self) ? "*" : "");
+            }
+        }
+    }
+
+    // Produces JSON structure.
+    private JsonNode json(ClusterService service, List<ControllerNode> nodes) {
+        ObjectMapper mapper = new ObjectMapper();
+        ArrayNode result = mapper.createArrayNode();
         ControllerNode self = service.getLocalNode();
         for (ControllerNode node : nodes) {
-            print(FMT, node.id(), node.ip(), node.tcpPort(),
-                  service.getState(node.id()),
-                  node.equals(self) ? "*" : "");
+            result.add(mapper.createObjectNode()
+                               .put("id", node.id().toString())
+                               .put("ip", node.ip().toString())
+                               .put("tcpPort", node.tcpPort())
+                               .put("state", service.getState(node.id()).toString())
+                               .put("self", node.equals(self)));
         }
+        return result;
     }
 
 }
diff --git a/cli/src/main/java/org/onlab/onos/cli/SummaryCommand.java b/cli/src/main/java/org/onlab/onos/cli/SummaryCommand.java
index 1597b55..180405b 100644
--- a/cli/src/main/java/org/onlab/onos/cli/SummaryCommand.java
+++ b/cli/src/main/java/org/onlab/onos/cli/SummaryCommand.java
@@ -1,5 +1,6 @@
 package org.onlab.onos.cli;
 
+import com.fasterxml.jackson.databind.ObjectMapper;
 import org.apache.karaf.shell.commands.Command;
 import org.onlab.onos.CoreService;
 import org.onlab.onos.cluster.ClusterService;
@@ -22,18 +23,32 @@
     protected void execute() {
         TopologyService topologyService = get(TopologyService.class);
         Topology topology = topologyService.currentTopology();
-        print("node=%s, version=%s",
-              get(ClusterService.class).getLocalNode().ip(),
-              get(CoreService.class).version().toString());
-        print("nodes=%d, devices=%d, links=%d, hosts=%d, clusters=%s, paths=%d, flows=%d, intents=%d",
-              get(ClusterService.class).getNodes().size(),
-              get(DeviceService.class).getDeviceCount(),
-              get(LinkService.class).getLinkCount(),
-              get(HostService.class).getHostCount(),
-              topologyService.getClusters(topology).size(),
-              topology.pathCount(),
-              get(FlowRuleService.class).getFlowRuleCount(),
-              get(IntentService.class).getIntentCount());
+        if (outputJson()) {
+            print("%s", new ObjectMapper().createObjectNode()
+                    .put("node", get(ClusterService.class).getLocalNode().ip().toString())
+                    .put("version", get(CoreService.class).version().toString())
+                    .put("nodes", get(ClusterService.class).getNodes().size())
+                    .put("devices", get(DeviceService.class).getDeviceCount())
+                    .put("links", get(LinkService.class).getLinkCount())
+                    .put("hosts", get(HostService.class).getHostCount())
+                    .put("clusters", topologyService.getClusters(topology).size())
+                    .put("paths", topology.pathCount())
+                    .put("flows", get(FlowRuleService.class).getFlowRuleCount())
+                    .put("intents", get(IntentService.class).getIntentCount()));
+        } else {
+            print("node=%s, version=%s",
+                  get(ClusterService.class).getLocalNode().ip(),
+                  get(CoreService.class).version().toString());
+            print("nodes=%d, devices=%d, links=%d, hosts=%d, clusters=%s, paths=%d, flows=%d, intents=%d",
+                  get(ClusterService.class).getNodes().size(),
+                  get(DeviceService.class).getDeviceCount(),
+                  get(LinkService.class).getLinkCount(),
+                  get(HostService.class).getHostCount(),
+                  topologyService.getClusters(topology).size(),
+                  topology.pathCount(),
+                  get(FlowRuleService.class).getFlowRuleCount(),
+                  get(IntentService.class).getIntentCount());
+        }
     }
 
 }
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/ClusterDevicesCommand.java b/cli/src/main/java/org/onlab/onos/cli/net/ClusterDevicesCommand.java
index f09b185..e03af45 100644
--- a/cli/src/main/java/org/onlab/onos/cli/net/ClusterDevicesCommand.java
+++ b/cli/src/main/java/org/onlab/onos/cli/net/ClusterDevicesCommand.java
@@ -1,5 +1,6 @@
 package org.onlab.onos.cli.net;
 
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.google.common.collect.Lists;
 import org.apache.karaf.shell.commands.Argument;
 import org.apache.karaf.shell.commands.Command;
@@ -10,6 +11,7 @@
 import java.util.Collections;
 import java.util.List;
 
+import static org.onlab.onos.cli.MastersListCommand.json;
 import static org.onlab.onos.net.topology.ClusterId.clusterId;
 
 /**
@@ -33,11 +35,14 @@
         } else {
             List<DeviceId> ids = Lists.newArrayList(service.getClusterDevices(topology, cluster));
             Collections.sort(ids, Comparators.ELEMENT_ID_COMPARATOR);
-            for (DeviceId deviceId : ids) {
-                print("%s", deviceId);
+            if (outputJson()) {
+                print("%s", json(new ObjectMapper(), ids));
+            } else {
+                for (DeviceId deviceId : ids) {
+                    print("%s", deviceId);
+                }
             }
         }
     }
 
-
 }
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/ClusterLinksCommand.java b/cli/src/main/java/org/onlab/onos/cli/net/ClusterLinksCommand.java
index 2bbfb46..ed5be77 100644
--- a/cli/src/main/java/org/onlab/onos/cli/net/ClusterLinksCommand.java
+++ b/cli/src/main/java/org/onlab/onos/cli/net/ClusterLinksCommand.java
@@ -5,6 +5,7 @@
 import org.onlab.onos.net.Link;
 import org.onlab.onos.net.topology.TopologyCluster;
 
+import static org.onlab.onos.cli.net.LinksListCommand.json;
 import static org.onlab.onos.cli.net.LinksListCommand.linkString;
 import static org.onlab.onos.net.topology.ClusterId.clusterId;
 
@@ -26,6 +27,8 @@
         TopologyCluster cluster = service.getCluster(topology, clusterId(cid));
         if (cluster == null) {
             error("No such cluster %s", cid);
+        } else if (outputJson()) {
+            print("%s", json(service.getClusterLinks(topology, cluster)));
         } else {
             for (Link link : service.getClusterLinks(topology, cluster)) {
                 print(linkString(link));
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/ClustersListCommand.java b/cli/src/main/java/org/onlab/onos/cli/net/ClustersListCommand.java
index 2b2953b..f41f85e 100644
--- a/cli/src/main/java/org/onlab/onos/cli/net/ClustersListCommand.java
+++ b/cli/src/main/java/org/onlab/onos/cli/net/ClustersListCommand.java
@@ -1,5 +1,8 @@
 package org.onlab.onos.cli.net;
 
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.google.common.collect.Lists;
 import org.apache.karaf.shell.commands.Command;
 import org.onlab.onos.cli.Comparators;
@@ -24,9 +27,26 @@
         List<TopologyCluster> clusters = Lists.newArrayList(service.getClusters(topology));
         Collections.sort(clusters, Comparators.CLUSTER_COMPARATOR);
 
-        for (TopologyCluster cluster : clusters) {
-            print(FMT, cluster.id().index(), cluster.deviceCount(), cluster.linkCount());
+        if (outputJson()) {
+            print("%s", json(clusters));
+        } else {
+            for (TopologyCluster cluster : clusters) {
+                print(FMT, cluster.id().index(), cluster.deviceCount(), cluster.linkCount());
+            }
         }
     }
 
+    // Produces a JSON result.
+    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()));
+        }
+        return result;
+    }
+
 }
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/DevicePortsListCommand.java b/cli/src/main/java/org/onlab/onos/cli/net/DevicePortsListCommand.java
index f66cedd..0044d0c 100644
--- a/cli/src/main/java/org/onlab/onos/cli/net/DevicePortsListCommand.java
+++ b/cli/src/main/java/org/onlab/onos/cli/net/DevicePortsListCommand.java
@@ -1,5 +1,9 @@
 package org.onlab.onos.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 org.apache.karaf.shell.commands.Argument;
 import org.apache.karaf.shell.commands.Command;
 import org.onlab.onos.cli.Comparators;
@@ -30,19 +34,61 @@
     protected void execute() {
         DeviceService service = get(DeviceService.class);
         if (uri == null) {
-            for (Device device : getSortedDevices(service)) {
-                printDevice(service, device);
+            if (outputJson()) {
+                print("%s", jsonPorts(service, getSortedDevices(service)));
+            } else {
+                for (Device device : getSortedDevices(service)) {
+                    printDevice(service, device);
+                }
             }
+
         } else {
             Device device = service.getDevice(deviceId(uri));
             if (device == null) {
                 error("No such device %s", uri);
+            } else if (outputJson()) {
+                print("%s", jsonPorts(service, new ObjectMapper(), device));
             } else {
                 printDevice(service, device);
             }
         }
     }
 
+    /**
+     * Produces JSON array containing ports of the specified devices.
+     *
+     * @param service device service
+     * @param devices collection of devices
+     * @return JSON array
+     */
+    public static JsonNode jsonPorts(DeviceService service, Iterable<Device> devices) {
+        ObjectMapper mapper = new ObjectMapper();
+        ArrayNode result = mapper.createArrayNode();
+        for (Device device : devices) {
+            result.add(jsonPorts(service, mapper, device));
+        }
+        return result;
+    }
+
+    /**
+     * Produces JSON array containing ports of the specified device.
+     *
+     * @param service device service
+     * @param mapper  object mapper
+     * @param device  infrastructure devices
+     * @return JSON array
+     */
+    public static JsonNode jsonPorts(DeviceService service, ObjectMapper mapper, Device device) {
+        ObjectNode result = mapper.createObjectNode();
+        ArrayNode ports = mapper.createArrayNode();
+        for (Port port : service.getPorts(device.id())) {
+            ports.add(mapper.createObjectNode()
+                              .put("port", port.number().toString())
+                              .put("isEnabled", port.isEnabled()));
+        }
+        return result.put("device", device.id().toString()).set("ports", ports);
+    }
+
     @Override
     protected void printDevice(DeviceService service, Device device) {
         super.printDevice(service, device);
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/DevicesListCommand.java b/cli/src/main/java/org/onlab/onos/cli/net/DevicesListCommand.java
index f34f97e..b7a8acc 100644
--- a/cli/src/main/java/org/onlab/onos/cli/net/DevicesListCommand.java
+++ b/cli/src/main/java/org/onlab/onos/cli/net/DevicesListCommand.java
@@ -1,5 +1,9 @@
 package org.onlab.onos.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 org.apache.karaf.shell.commands.Command;
 import org.onlab.onos.cli.AbstractShellCommand;
 import org.onlab.onos.cli.Comparators;
@@ -24,12 +28,55 @@
     @Override
     protected void execute() {
         DeviceService service = get(DeviceService.class);
-        for (Device device : getSortedDevices(service)) {
-            printDevice(service, device);
+        if (outputJson()) {
+            print("%s", json(service, getSortedDevices(service)));
+        } else {
+            for (Device device : getSortedDevices(service)) {
+                printDevice(service, device);
+            }
         }
     }
 
     /**
+     * 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) {
+        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("role", service.getRole(device.id()).toString())
+                    .put("mfr", device.manufacturer())
+                    .put("hw", device.hwVersion())
+                    .put("sw", device.swVersion())
+                    .put("serial", device.serialNumber());
+        }
+        return result;
+    }
+
+    /**
      * Returns the list of devices sorted using the device ID URIs.
      *
      * @param service device service
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/HostsListCommand.java b/cli/src/main/java/org/onlab/onos/cli/net/HostsListCommand.java
index cd9ba08..f431142 100644
--- a/cli/src/main/java/org/onlab/onos/cli/net/HostsListCommand.java
+++ b/cli/src/main/java/org/onlab/onos/cli/net/HostsListCommand.java
@@ -1,10 +1,15 @@
 package org.onlab.onos.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 org.apache.karaf.shell.commands.Command;
 import org.onlab.onos.cli.AbstractShellCommand;
 import org.onlab.onos.cli.Comparators;
 import org.onlab.onos.net.Host;
 import org.onlab.onos.net.host.HostService;
+import org.onlab.packet.IpPrefix;
 
 import java.util.Collections;
 import java.util.List;
@@ -15,7 +20,7 @@
  * Lists all currently-known hosts.
  */
 @Command(scope = "onos", name = "hosts",
-        description = "Lists all currently-known hosts.")
+         description = "Lists all currently-known hosts.")
 public class HostsListCommand extends AbstractShellCommand {
 
     private static final String FMT =
@@ -24,11 +29,42 @@
     @Override
     protected void execute() {
         HostService service = get(HostService.class);
-        for (Host host : getSortedHosts(service)) {
-            printHost(host);
+        if (outputJson()) {
+            print("%s", json(getSortedHosts(service)));
+        } else {
+            for (Host host : getSortedHosts(service)) {
+                printHost(host);
+            }
         }
     }
 
+    // Produces JSON structure.
+    private static 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 (IpPrefix 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);
+        return result;
+    }
+
     /**
      * Returns the list of devices sorted using the device ID URIs.
      *
@@ -44,14 +80,14 @@
     /**
      * Prints information about a host.
      *
-     * @param host
+     * @param host end-station host
      */
     protected void printHost(Host host) {
         if (host != null) {
             print(FMT, host.id(), host.mac(),
-                    host.location().deviceId(),
-                    host.location().port(),
-                    host.vlan(), host.ipAddresses());
+                  host.location().deviceId(),
+                  host.location().port(),
+                  host.vlan(), host.ipAddresses());
         }
     }
- }
+}
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/LinksListCommand.java b/cli/src/main/java/org/onlab/onos/cli/net/LinksListCommand.java
index f5226b1..59d2be7 100644
--- a/cli/src/main/java/org/onlab/onos/cli/net/LinksListCommand.java
+++ b/cli/src/main/java/org/onlab/onos/cli/net/LinksListCommand.java
@@ -1,8 +1,13 @@
 package org.onlab.onos.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 org.apache.karaf.shell.commands.Argument;
 import org.apache.karaf.shell.commands.Command;
 import org.onlab.onos.cli.AbstractShellCommand;
+import org.onlab.onos.net.ConnectPoint;
 import org.onlab.onos.net.Link;
 import org.onlab.onos.net.link.LinkService;
 
@@ -27,12 +32,58 @@
         LinkService service = get(LinkService.class);
         Iterable<Link> links = uri != null ?
                 service.getDeviceLinks(deviceId(uri)) : service.getLinks();
-        for (Link link : links) {
-            print(linkString(link));
+        if (outputJson()) {
+            print("%s", json(links));
+        } else {
+            for (Link link : links) {
+                print(linkString(link));
+            }
         }
     }
 
     /**
+     * Produces a JSON array containing the specified links.
+     *
+     * @param links collection of links
+     * @return JSON array
+     */
+    public static JsonNode json(Iterable<Link> links) {
+        ObjectMapper mapper = new ObjectMapper();
+        ArrayNode result = mapper.createArrayNode();
+        for (Link link : links) {
+            result.add(json(mapper, link));
+        }
+        return result;
+    }
+
+    /**
+     * Produces a JSON object for the specified link.
+     *
+     * @param mapper object mapper
+     * @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.src()));
+        return result;
+    }
+
+    /**
+     * 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());
+    }
+
+    /**
      * Returns a formatted string representing the given link.
      *
      * @param link infrastructure link
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/TopologyCommand.java b/cli/src/main/java/org/onlab/onos/cli/net/TopologyCommand.java
index 5c8310f..a7e6422 100644
--- a/cli/src/main/java/org/onlab/onos/cli/net/TopologyCommand.java
+++ b/cli/src/main/java/org/onlab/onos/cli/net/TopologyCommand.java
@@ -1,5 +1,6 @@
 package org.onlab.onos.cli.net;
 
+import com.fasterxml.jackson.databind.ObjectMapper;
 import org.apache.karaf.shell.commands.Command;
 import org.onlab.onos.cli.AbstractShellCommand;
 import org.onlab.onos.net.topology.Topology;
@@ -30,8 +31,17 @@
     @Override
     protected void execute() {
         init();
-        print(FMT, topology.time(), topology.deviceCount(), topology.linkCount(),
-              topology.clusterCount(), topology.pathCount());
+        if (outputJson()) {
+            print("%s", new ObjectMapper().createObjectNode()
+                    .put("time", topology.time())
+                    .put("deviceCount", topology.deviceCount())
+                    .put("linkCount", topology.linkCount())
+                    .put("clusterCount", topology.clusterCount())
+                    .put("pathCount", topology.pathCount()));
+        } else {
+            print(FMT, topology.time(), topology.deviceCount(), topology.linkCount(),
+                  topology.clusterCount(), topology.pathCount());
+        }
     }
 
 }