Add support to decode Device, Port, Link JSON.

- Device, Port, Link can now be encoded and decoded back to Java Object,
  which will be Object#equals to the original.
- Modified DeviceServiceAdapter to be null-safe when possible
- Modified JSON assertion/matcher not to check for exact number of attributes

Change-Id: I7cf02e2254cf17f6265fb15847912519e564b14f
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/AnnotatedCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/AnnotatedCodec.java
index a3d0663..6883fe0 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/AnnotatedCodec.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/AnnotatedCodec.java
@@ -16,10 +16,12 @@
 package org.onosproject.codec.impl;
 
 import com.fasterxml.jackson.databind.node.ObjectNode;
+
 import org.onosproject.codec.CodecContext;
 import org.onosproject.codec.JsonCodec;
 import org.onosproject.net.Annotated;
 import org.onosproject.net.Annotations;
+import org.onosproject.net.DefaultAnnotations;
 
 /**
  * Base JSON codec for annotated entities.
@@ -42,4 +44,21 @@
         return node;
     }
 
+    /**
+     * Extracts annotations of given Object.
+     *
+     * @param objNode annotated JSON object node
+     * @param context decode context
+     * @return extracted Annotations
+     */
+    protected Annotations extractAnnotations(ObjectNode objNode, CodecContext context) {
+
+        JsonCodec<Annotations> codec = context.codec(Annotations.class);
+        if (objNode.has("annotations") && objNode.isObject()) {
+            return codec.decode((ObjectNode) objNode.get("annotations"), context);
+        } else {
+            return DefaultAnnotations.EMPTY;
+        }
+    }
+
 }