[OLINGO-1485]Server side support for instance annotations
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/json/ODataJsonDeserializer.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/json/ODataJsonDeserializer.java
index 9b4fc8e..babc5a0 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/json/ODataJsonDeserializer.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/json/ODataJsonDeserializer.java
@@ -39,6 +39,7 @@
 import org.apache.olingo.commons.api.IConstants;
 import org.apache.olingo.commons.api.constants.Constantsv00;
 import org.apache.olingo.commons.api.constants.Constantsv01;
+import org.apache.olingo.commons.api.data.Annotation;
 import org.apache.olingo.commons.api.data.ComplexValue;
 import org.apache.olingo.commons.api.data.DeletedEntity;
 import org.apache.olingo.commons.api.data.DeletedEntity.Reason;
@@ -118,6 +119,7 @@
   private final boolean isIEEE754Compatible;
   private ServiceMetadata serviceMetadata;
   private IConstants constants;
+  private ODataJsonInstanceAnnotationDeserializer instanceAnnotDeserializer;
 
   public ODataJsonDeserializer(final ContentType contentType) {
     this(contentType, null, new Constantsv00());
@@ -127,17 +129,20 @@
     isIEEE754Compatible = ContentTypeHelper.isODataIEEE754Compatible(contentType);
     this.serviceMetadata = serviceMetadata;
     this.constants = new Constantsv00();
+    instanceAnnotDeserializer = new ODataJsonInstanceAnnotationDeserializer();
   }
 
   public ODataJsonDeserializer(ContentType contentType, ServiceMetadata serviceMetadata, IConstants constants) {
     isIEEE754Compatible = ContentTypeHelper.isODataIEEE754Compatible(contentType);
     this.serviceMetadata = serviceMetadata;
     this.constants = constants;
+    instanceAnnotDeserializer = new ODataJsonInstanceAnnotationDeserializer();
   }
 
   public ODataJsonDeserializer(ContentType contentType, IConstants constants) {
     isIEEE754Compatible = ContentTypeHelper.isODataIEEE754Compatible(contentType);
     this.constants = constants;
+    instanceAnnotDeserializer = new ODataJsonInstanceAnnotationDeserializer();
   }
 
   @Override
@@ -451,6 +456,29 @@
         Link bindingLink = consumeBindingLink(field.getKey(), field.getValue(), edmEntityType);
         entity.getNavigationBindings().add(bindingLink);
         toRemove.add(field.getKey());
+      } else if (!field.getKey().contains(ODATA_CONTROL_INFORMATION_PREFIX) && 
+    		  field.getKey().contains(ODATA_ANNOTATION_MARKER) &&
+    		  field.getKey().substring(field.getKey().indexOf(ODATA_ANNOTATION_MARKER))
+    		  .contains(".")) {
+    	// Instance annotations start with @ sign followed by 
+          // alias or namespace 
+          // followed by a dot and then term name
+    	  String[] keySplit = field.getKey().split(ODATA_ANNOTATION_MARKER);
+    	  String termName = keySplit[1];
+    	  Annotation annotation = instanceAnnotDeserializer.consumeInstanceAnnotation(termName, field.getValue());
+    	  // If keySplit has a value at zeroth index then instance annotation is specified like 
+    	  // propertyName@Term
+    	  if (!keySplit[0].isEmpty()) {
+    		  if (edmEntityType.getPropertyNames().contains(keySplit[0])) {
+    			  entity.getProperty(keySplit[0]).getAnnotations().add(annotation);
+    		  } else if (edmEntityType.getNavigationPropertyNames().contains(keySplit[0])) {
+    			  Link link = entity.getNavigationLink(keySplit[0]);
+    			  link.getAnnotations().add(annotation);
+    		  }
+    	  } else {
+    		  entity.getAnnotations().add(annotation);
+    	  }
+    	  toRemove.add(field.getKey());
       }
     }
     // remove here to avoid iterator issues.
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/json/ODataJsonInstanceAnnotationDeserializer.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/json/ODataJsonInstanceAnnotationDeserializer.java
new file mode 100644
index 0000000..0ac5251
--- /dev/null
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/json/ODataJsonInstanceAnnotationDeserializer.java
@@ -0,0 +1,234 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements. See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership. The ASF licenses this file

+ * to you under the Apache License, Version 2.0 (the

+ * "License"); you may not use this file except in compliance

+ * with the License. You may obtain a copy of the License at

+ *

+ * http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing,

+ * software distributed under the License is distributed on an

+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY

+ * KIND, either express or implied. See the License for the

+ * specific language governing permissions and limitations

+ * under the License.

+ */

+package org.apache.olingo.server.core.deserializer.json;

+

+import java.io.IOException;

+import java.util.ArrayList;

+import java.util.HashSet;

+import java.util.Iterator;

+import java.util.List;

+import java.util.Map;

+import java.util.Set;

+import java.util.AbstractMap.SimpleEntry;

+

+import org.apache.olingo.commons.api.Constants;

+import org.apache.olingo.commons.api.data.Annotation;

+import org.apache.olingo.commons.api.data.ComplexValue;

+import org.apache.olingo.commons.api.data.Property;

+import org.apache.olingo.commons.api.data.PropertyType;

+import org.apache.olingo.commons.api.data.Valuable;

+import org.apache.olingo.commons.api.data.ValueType;

+import org.apache.olingo.commons.api.edm.EdmPrimitiveType;

+import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException;

+import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;

+import org.apache.olingo.commons.api.edm.geo.Geospatial;

+import org.apache.olingo.commons.core.edm.EdmTypeInfo;

+import org.apache.olingo.server.api.deserializer.DeserializerException;

+

+import com.fasterxml.jackson.databind.JsonNode;

+import com.fasterxml.jackson.databind.node.ObjectNode;

+

+public class ODataJsonInstanceAnnotationDeserializer {

+

+	private static final String ODATA_CONTROL_INFORMATION_PREFIX = "@odata.";

+	

+	/**

+	 * Consume the instance annotation of an entity or a property

+	 * @param key String

+	 * @param value JsonNode

+	 * @return Annotation of an entity

+	 * @throws DeserializerException

+	 */

+	public Annotation consumeInstanceAnnotation(String key, JsonNode value) 

+			throws DeserializerException {

+		Annotation annotation = new Annotation();

+		annotation.setTerm(key);

+		try {

+			value(annotation, value);

+		} catch (EdmPrimitiveTypeException | IOException e) {

+			throw new DeserializerException("Property: " + key,

+					DeserializerException.MessageKeys.INVALID_VALUE_FOR_PROPERTY, key);

+		}

+		return annotation;

+	}

+

+	private void value(final Valuable valuable, final JsonNode node) 

+			throws IOException, EdmPrimitiveTypeException {

+

+		EdmTypeInfo typeInfo = null;

+

+		final Map.Entry<PropertyType, EdmTypeInfo> guessed = guessPropertyType(node);

+		if (typeInfo == null) {

+			typeInfo = guessed.getValue();

+		}

+

+		final PropertyType propType = typeInfo == null ? guessed.getKey()

+				: typeInfo.isCollection() ? PropertyType.COLLECTION

+				: typeInfo.isPrimitiveType() ? PropertyType.PRIMITIVE

+				: node.isValueNode() ? PropertyType.ENUM : PropertyType.COMPLEX;

+

+		switch (propType) {

+		case COLLECTION:

+			fromCollection(valuable, node.elements(), typeInfo);

+			break;

+

+		case COMPLEX:

+			if (node.has(Constants.JSON_TYPE)) {

+				valuable.setType(node.get(Constants.JSON_TYPE).asText());

+				((ObjectNode) node).remove(Constants.JSON_TYPE);

+			}

+			final Object value = fromComplex((ObjectNode) node);

+			if (value instanceof ComplexValue) {

+				((ComplexValue) value).setTypeName(valuable.getType());

+			}

+			valuable.setValue(ValueType.COMPLEX, value);

+			break;

+

+		case ENUM:

+			if (!node.isNull()) {

+				valuable.setValue(ValueType.ENUM, node.asText());

+			}

+			break;

+

+		case PRIMITIVE:

+			if (valuable.getType() == null && typeInfo != null) {

+				valuable.setType(typeInfo.getFullQualifiedName().toString());

+			}

+			final Object primitiveValue = fromPrimitive(node, typeInfo);

+			valuable.setValue(primitiveValue instanceof Geospatial ? 

+					ValueType.GEOSPATIAL : ValueType.PRIMITIVE,

+					primitiveValue);

+			break;

+

+		case EMPTY:

+		default:

+			valuable.setValue(ValueType.PRIMITIVE, "");

+		}

+	}

+

+	private Object fromPrimitive(final JsonNode node, final EdmTypeInfo typeInfo) 

+			throws EdmPrimitiveTypeException {

+		return node.isNull() ? null

+				: typeInfo == null ? node.asText()

+				: ((EdmPrimitiveType) typeInfo.getType()).valueOfString(node.asText(), true, null,

+				Constants.DEFAULT_PRECISION, Constants.DEFAULT_SCALE, true,

+				((EdmPrimitiveType) typeInfo.getType()).getDefaultType());

+	}

+

+	private Object fromComplex(final ObjectNode node) throws IOException, EdmPrimitiveTypeException {

+

+		final ComplexValue complexValue = new ComplexValue();

+		final Set<String> toRemove = new HashSet<>();

+		for (final Iterator<Map.Entry<String, JsonNode>> itor = node.fields(); itor.hasNext();) {

+			final Map.Entry<String, JsonNode> field = itor.next();

+			if (field.getKey().contains(ODATA_CONTROL_INFORMATION_PREFIX)) {

+				toRemove.add(field.getKey());

+			} else {

+				Property property = new Property();

+				property.setName(field.getKey());

+				value(property, field.getValue());

+				complexValue.getValue().add(property);

+			}

+		}

+		node.remove(toRemove);

+		return complexValue;

+	}

+

+	private void fromCollection(final Valuable valuable, final Iterator<JsonNode> nodeItor, 

+			final EdmTypeInfo typeInfo)

+			throws IOException, EdmPrimitiveTypeException {

+

+		final List<Object> values = new ArrayList<>();

+		ValueType valueType = ValueType.COLLECTION_PRIMITIVE;

+

+		final EdmTypeInfo type = typeInfo == null ? null

+				: new EdmTypeInfo.Builder().setTypeExpression(

+						typeInfo.getFullQualifiedName().toString()).build();

+

+		while (nodeItor.hasNext()) {

+			final JsonNode child = nodeItor.next();

+

+			if (child.isValueNode()) {

+				if (typeInfo == null || typeInfo.isPrimitiveType()) {

+					final Object value = fromPrimitive(child, type);

+					valueType = value instanceof Geospatial ? ValueType.COLLECTION_GEOSPATIAL

+							: ValueType.COLLECTION_PRIMITIVE;

+					values.add(value);

+				} else {

+					valueType = ValueType.COLLECTION_ENUM;

+					values.add(child.asText());

+				}

+			} else if (child.isContainerNode()) {

+				EdmTypeInfo childType = null;

+				if (child.has(Constants.JSON_TYPE)) {

+					String typeName = child.get(Constants.JSON_TYPE).asText();

+					childType = typeName == null ? null : new EdmTypeInfo.Builder()

+							.setTypeExpression(typeName).build();

+					((ObjectNode) child).remove(Constants.JSON_TYPE);

+				}

+				final Object value = fromComplex((ObjectNode) child);

+				if (childType != null) {

+					((ComplexValue) value).setTypeName(childType.external());

+				}

+				valueType = ValueType.COLLECTION_COMPLEX;

+				values.add(value);

+			}

+		}

+		valuable.setValue(valueType, values);

+	}

+

+	private Map.Entry<PropertyType, EdmTypeInfo> guessPropertyType(final JsonNode node) {

+		PropertyType type;

+		String typeExpression = null;

+

+		if (node.isValueNode() || node.isNull()) {

+			type = PropertyType.PRIMITIVE;

+			typeExpression = guessPrimitiveTypeKind(node).getFullQualifiedName().toString();

+		} else if (node.isArray()) {

+			type = PropertyType.COLLECTION;

+			if (node.has(0) && node.get(0).isValueNode()) {

+				typeExpression = "Collection(" + guessPrimitiveTypeKind(node.get(0)) + ')';

+			}

+		} else if (node.isObject()) {

+			if (node.has(Constants.ATTR_TYPE)) {

+				type = PropertyType.PRIMITIVE;

+				typeExpression = "Edm.Geography" + node.get(Constants.ATTR_TYPE).asText();

+			} else {

+				type = PropertyType.COMPLEX;

+			}

+		} else {

+			type = PropertyType.EMPTY;

+		}

+

+		final EdmTypeInfo typeInfo = typeExpression == null ? null

+				: new EdmTypeInfo.Builder().setTypeExpression(typeExpression).build();

+		return new SimpleEntry<>(type, typeInfo);

+	}

+

+	private EdmPrimitiveTypeKind guessPrimitiveTypeKind(final JsonNode node) {

+		return node.isShort() ? EdmPrimitiveTypeKind.Int16

+				: node.isInt() ? EdmPrimitiveTypeKind.Int32

+				: node.isLong() ? EdmPrimitiveTypeKind.Int64

+				: node.isBoolean() ? EdmPrimitiveTypeKind.Boolean

+				: node.isFloat() ? EdmPrimitiveTypeKind.Single

+				: node.isDouble() ? EdmPrimitiveTypeKind.Double

+				: node.isBigDecimal() ? EdmPrimitiveTypeKind.Decimal

+				: EdmPrimitiveTypeKind.String;

+	}

+}

diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonInstanceAnnotationSerializer.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonInstanceAnnotationSerializer.java
new file mode 100644
index 0000000..d197f99
--- /dev/null
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonInstanceAnnotationSerializer.java
@@ -0,0 +1,230 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements. See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership. The ASF licenses this file

+ * to you under the Apache License, Version 2.0 (the

+ * "License"); you may not use this file except in compliance

+ * with the License. You may obtain a copy of the License at

+ *

+ * http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing,

+ * software distributed under the License is distributed on an

+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY

+ * KIND, either express or implied. See the License for the

+ * specific language governing permissions and limitations

+ * under the License.

+ */

+package org.apache.olingo.server.core.serializer.json;

+

+import java.io.IOException;

+import java.util.List;

+

+import org.apache.commons.codec.DecoderException;

+import org.apache.olingo.commons.api.Constants;

+import org.apache.olingo.commons.api.IConstants;

+import org.apache.olingo.commons.api.data.Annotation;

+import org.apache.olingo.commons.api.data.ComplexValue;

+import org.apache.olingo.commons.api.data.Link;

+import org.apache.olingo.commons.api.data.Property;

+import org.apache.olingo.commons.api.data.Valuable;

+import org.apache.olingo.commons.api.edm.EdmPrimitiveType;

+import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException;

+import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;

+import org.apache.olingo.commons.api.edm.EdmProperty;

+import org.apache.olingo.commons.api.format.ContentType;

+import org.apache.olingo.commons.core.edm.primitivetype.EdmPrimitiveTypeFactory;

+import org.apache.olingo.server.api.serializer.SerializerException;

+import org.apache.olingo.server.core.serializer.utils.ContentTypeHelper;

+

+import com.fasterxml.jackson.core.JsonGenerator;

+

+public class ODataJsonInstanceAnnotationSerializer {

+

+	private final boolean isODataMetadataNone;

+	private final boolean isODataMetadataFull;

+	private IConstants constants;

+	private final boolean isIEEE754Compatible;

+

+	public ODataJsonInstanceAnnotationSerializer(final ContentType contentType, final IConstants constants) {

+		isIEEE754Compatible = ContentTypeHelper.isODataIEEE754Compatible(contentType);

+		isODataMetadataNone = ContentTypeHelper.isODataMetadataNone(contentType);

+		isODataMetadataFull = ContentTypeHelper.isODataMetadataFull(contentType);

+		this.constants = constants;

+	}

+

+	/**

+	 * Write the instance annotation of an entity

+	 * @param annotations List of annotations

+	 * @param json JsonGenerator

+	 * @throws IOException 

+	 * @throws SerializerException

+	 * @throws DecoderException

+	 */

+	public void writeInstanceAnnotationsOnEntity(final List<Annotation> annotations, final JsonGenerator json)

+			throws IOException, SerializerException, DecoderException {

+		for (Annotation annotation : annotations) {

+			if (isODataMetadataFull) {

+				json.writeStringField(constants.getType(), "#" + annotation.getType());

+			}

+			json.writeFieldName("@" + annotation.getTerm());

+			writeInstanceAnnotation(json, annotation, "");

+		}

+	}

+

+	/**

+	 * Write instance annotation of a property

+	 * @param edmProperty EdmProperty

+	 * @param property Property

+	 * @param json JsonGenerator

+	 * @throws IOException

+	 * @throws SerializerException

+	 * @throws DecoderException

+	 */

+	public void writeInstanceAnnotationsOnProperties(final EdmProperty edmProperty, final Property property,

+			final JsonGenerator json) throws IOException, SerializerException, DecoderException {

+		if (property != null) {

+			for (Annotation annotation : property.getAnnotations()) {

+				json.writeFieldName(edmProperty.getName() + "@" + annotation.getTerm());

+				writeInstanceAnnotation(json, annotation, "");

+			}

+		}

+	}

+

+	@SuppressWarnings({ "unchecked", "rawtypes" })

+	private void writeInstanceAnnotation(final JsonGenerator json, Valuable annotation, String name)

+			throws IOException, SerializerException, DecoderException {

+		try {

+			switch (annotation.getValueType()) {

+			case PRIMITIVE:

+				if (isODataMetadataFull && name.length() > 0) {

+					json.writeStringField(name + constants.getType(), "#" + annotation.getType());

+				}

+				if (name.length() > 0) {

+					json.writeFieldName(name);

+				}

+				writeInstanceAnnotOnPrimitiveProperty(json, annotation, annotation.getValue());

+				break;

+			case COLLECTION_PRIMITIVE:

+				if (isODataMetadataFull && name.length() > 0) {

+					json.writeStringField(name + constants.getType(), 

+							"#Collection(" + annotation.getType() + ")");

+				}

+				if (name.length() > 0) {

+					json.writeFieldName(name);

+				}

+				json.writeStartArray();

+				List list = annotation.asCollection();

+				for (Object value : list) {

+					writeInstanceAnnotOnPrimitiveProperty(json, annotation, value);

+				}

+				json.writeEndArray();

+				break;

+			case COMPLEX:

+				if (isODataMetadataFull && name.length() > 0) {

+					json.writeStringField(name + constants.getType(), "#" + annotation.getType());

+				}

+				if (name.length() > 0) {

+					json.writeFieldName(name);

+				}

+				ComplexValue complexValue = annotation.asComplex();

+				writeInstanceAnnotOnComplexProperty(json, annotation, complexValue);

+				break;

+			case COLLECTION_COMPLEX:

+				if (isODataMetadataFull && name.length() > 0) {

+					json.writeStringField(name + constants.getType(), 

+							"#Collection(" + annotation.getType() + ")");

+				}

+				if (name.length() > 0) {

+					json.writeFieldName(name);

+				}

+				json.writeStartArray();

+				List<ComplexValue> complexValues = (List<ComplexValue>) annotation.asCollection();

+				for (ComplexValue complxValue : complexValues) {

+					writeInstanceAnnotOnComplexProperty(json, annotation, complxValue);

+				}

+				json.writeEndArray();

+				break;

+			default:

+			}

+		} catch (final EdmPrimitiveTypeException e) {

+			throw new SerializerException("Wrong value for instance annotation!", e,

+					SerializerException.MessageKeys.WRONG_PROPERTY_VALUE, 

+					((Annotation) annotation).getTerm(),

+					annotation.getValue().toString());

+		}

+	}

+

+	private void writeInstanceAnnotOnComplexProperty(final JsonGenerator json, Valuable annotation,

+			ComplexValue complexValue) throws IOException, SerializerException, DecoderException {

+		json.writeStartObject();

+		if (isODataMetadataFull) {

+			json.writeStringField(constants.getType(), "#" + complexValue.getTypeName());

+		}

+		List<Property> properties = complexValue.getValue();

+		for (Property prop : properties) {

+			writeInstanceAnnotation(json, prop, prop.getName());

+		}

+		json.writeEndObject();

+	}

+

+	private void writeInstanceAnnotOnPrimitiveProperty(final JsonGenerator json, Valuable annotation, Object value)

+			throws IOException, EdmPrimitiveTypeException {

+		writePrimitiveValue("",

+				EdmPrimitiveTypeFactory.getInstance(

+						EdmPrimitiveTypeKind.getByName(annotation.getType())), value, null,

+				null, null, null, true, json);

+	}

+

+	protected void writePrimitiveValue(final String name, final EdmPrimitiveType type, final Object primitiveValue,

+			final Boolean isNullable, final Integer maxLength, final Integer precision, final Integer scale,

+			final Boolean isUnicode, final JsonGenerator json) 

+					throws EdmPrimitiveTypeException, IOException {

+		final String value = type.valueToString(

+				primitiveValue, isNullable, maxLength, precision, scale, isUnicode);

+		if (value == null) {

+			json.writeNull();

+		} else if (type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Boolean)) {

+			json.writeBoolean(Boolean.parseBoolean(value));

+		} else if (type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Byte)

+				|| type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Double)

+				|| type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Int16)

+				|| type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Int32)

+				|| type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.SByte)

+				|| type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Single)

+				|| (type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal)

+				|| type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Int64))

+						&& !isIEEE754Compatible) {

+			json.writeNumber(value);

+		} else if (type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Stream)) {

+			if (primitiveValue instanceof Link) {

+				Link stream = (Link) primitiveValue;

+				if (!isODataMetadataNone) {

+					if (stream.getMediaETag() != null) {

+						json.writeStringField(name + constants.getMediaEtag(), 

+								stream.getMediaETag());

+					}

+					if (stream.getType() != null) {

+						json.writeStringField(name + constants.getMediaContentType(), 

+								stream.getType());

+					}

+				}

+				if (isODataMetadataFull) {

+					if (stream.getRel() != null && 

+							stream.getRel().equals(Constants.NS_MEDIA_READ_LINK_REL)) {

+						json.writeStringField(name + constants.getMediaReadLink(), 

+								stream.getHref());

+					}

+					if (stream.getRel() == null || 

+							stream.getRel().equals(Constants.NS_MEDIA_EDIT_LINK_REL)) {

+						json.writeStringField(name + constants.getMediaEditLink(), 

+								stream.getHref());

+					}

+				}

+			}

+		} else {

+			json.writeString(value);

+		}

+	}

+}

diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java
index e9fdfd7..ec75da0 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java
@@ -115,12 +115,14 @@
   private final boolean isODataMetadataNone;
   private final boolean isODataMetadataFull;
   private IConstants constants;
+  private ODataJsonInstanceAnnotationSerializer instanceAnnotSerializer;
 
   public ODataJsonSerializer(final ContentType contentType, final IConstants constants) {
     isIEEE754Compatible = ContentTypeHelper.isODataIEEE754Compatible(contentType);
     isODataMetadataNone = ContentTypeHelper.isODataMetadataNone(contentType);
     isODataMetadataFull = ContentTypeHelper.isODataMetadataFull(contentType);
     this.constants = constants;
+    instanceAnnotSerializer = new ODataJsonInstanceAnnotationSerializer(contentType, constants);
   }
 
   public ODataJsonSerializer(final ContentType contentType) {
@@ -128,6 +130,7 @@
     isODataMetadataNone = ContentTypeHelper.isODataMetadataNone(contentType);
     isODataMetadataFull = ContentTypeHelper.isODataMetadataFull(contentType);
     this.constants = new Constantsv00();
+    instanceAnnotSerializer = new ODataJsonInstanceAnnotationSerializer(contentType, constants);
   }
 
   @Override
@@ -431,7 +434,7 @@
             json.writeStringField(constants.getEditLink(), entity.getEditLink().getHref());
           }
         }
-        
+        instanceAnnotSerializer.writeInstanceAnnotationsOnEntity(entity.getAnnotations(), json);        
         writeProperties(metadata, resolvedType, entity.getProperties(), select, json, entity, expand);
         writeNavigationProperties(metadata, resolvedType, entity, expand, toDepth, ancestors, name, json);
         writeOperations(entity.getOperations(), json);      
@@ -507,7 +510,7 @@
   protected void writeProperties(final ServiceMetadata metadata, final EdmStructuredType type,
       final List<Property> properties,
       final SelectOption select, final JsonGenerator json, Linked linked, ExpandOption expand)
-      throws IOException, SerializerException {
+      throws IOException, SerializerException, DecoderException {
     final boolean all = ExpandSelectHelper.isAll(select);
     final Set<String> selected = all ? new HashSet<>() :
         ExpandSelectHelper.getSelectedPropertyNames(select.getSelectItems());
@@ -679,7 +682,9 @@
       final EdmProperty edmProperty, final Property property,
       final Set<List<String>> selectedPaths, final JsonGenerator json, 
       Set<List<String>> expandedPaths, Linked linked, ExpandOption expand)
-      throws IOException, SerializerException {
+      throws IOException, SerializerException, DecoderException  {
+	
+	instanceAnnotSerializer.writeInstanceAnnotationsOnProperties(edmProperty, property, json);
     boolean isStreamProperty = isStreamProperty(edmProperty);
     writePropertyType(edmProperty, json);
     if (!isStreamProperty) {
@@ -745,7 +750,7 @@
   private void writePropertyValue(final ServiceMetadata metadata, final EdmProperty edmProperty,
       final Property property, final Set<List<String>> selectedPaths, final JsonGenerator json, 
       Set<List<String>> expandedPaths, Linked linked, ExpandOption expand)
-      throws IOException, SerializerException {
+      throws IOException, SerializerException, DecoderException {
     final EdmType type = edmProperty.getType();
     try {
       if (edmProperty.isPrimitive()
@@ -790,7 +795,7 @@
   private void writeComplex(final ServiceMetadata metadata, final EdmComplexType type,
       final Property property, final Set<List<String>> selectedPaths, final JsonGenerator json, 
       Set<List<String>> expandedPaths, Linked linked, ExpandOption expand) 
-          throws IOException, SerializerException{
+          throws IOException, SerializerException, DecoderException{
         json.writeStartObject();        
         String derivedName = property.getType();
         EdmComplexType resolvedType = null;
@@ -864,7 +869,7 @@
       final Property property,
       final Set<List<String>> selectedPaths, final JsonGenerator json, 
       Set<List<String>> expandedPaths, Linked linked, ExpandOption expand)
-      throws IOException, SerializerException {
+      throws IOException, SerializerException, DecoderException {
     json.writeStartArray();
     EdmComplexType derivedType = type;
     Set<List<String>> expandedPaths1 = expandedPaths != null && !expandedPaths.isEmpty() ? 
@@ -1063,7 +1068,7 @@
       final EdmComplexType type, final List<Property> properties,
       final Set<List<String>> selectedPaths, final JsonGenerator json, 
       Set<List<String>> expandedPaths, Linked linked, ExpandOption expand, String complexPropName)
-      throws IOException, SerializerException {
+      throws IOException, SerializerException, DecoderException {
 
     if (null != expandedPaths) {
       for(List<String> paths : expandedPaths) {
@@ -1237,7 +1242,8 @@
 
   @Override
   public SerializerResult complexCollection(final ServiceMetadata metadata, final EdmComplexType type,
-      final Property property, final ComplexSerializerOptions options) throws SerializerException {
+      final Property property, final ComplexSerializerOptions options) 
+    		  throws SerializerException {
     OutputStream outputStream = null;
     SerializerException cachedException = null;
     
@@ -1270,7 +1276,7 @@
 
       json.close();
       return SerializerResultImpl.with().content(buffer.getInputStream()).build();
-    } catch (final IOException e) {
+    } catch (final IOException | DecoderException e) {
       cachedException =
           new SerializerException(IO_EXCEPTION_TEXT, e, SerializerException.MessageKeys.IO_EXCEPTION);
       throw cachedException;
diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/deserializer/json/ODataDeserializerDeepInsertTest.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/deserializer/json/ODataDeserializerDeepInsertTest.java
index e684c0f..d673af5 100644
--- a/lib/server-test/src/test/java/org/apache/olingo/server/core/deserializer/json/ODataDeserializerDeepInsertTest.java
+++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/deserializer/json/ODataDeserializerDeepInsertTest.java
@@ -21,13 +21,15 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
-import static org.junit.Assert.fail;
 
 import java.io.IOException;
+import java.util.List;
 
 import org.apache.olingo.commons.api.Constants;
+import org.apache.olingo.commons.api.data.Annotation;
 import org.apache.olingo.commons.api.data.Entity;
 import org.apache.olingo.commons.api.data.Link;
+import org.apache.olingo.commons.api.data.ValueType;
 import org.apache.olingo.commons.api.format.ContentType;
 import org.apache.olingo.server.api.deserializer.DeserializerException;
 import org.apache.olingo.server.api.deserializer.DeserializerResult;
@@ -126,22 +128,24 @@
 
   @Test
   public void esAllPrimExpandedToOneWithCustomAnnotations() throws Exception {
-    try {
-      deserialize("EntityESAllPrimExpandedNavPropertyETTwoPrimOneWithCustomAnnotations.json");
-      fail("Expected exception not thrown.");
-    } catch (final DeserializerException e) {
-      assertEquals(DeserializerException.MessageKeys.NOT_IMPLEMENTED, e.getMessageKey());
-    }
+      Entity entity = deserialize("EntityESAllPrimExpandedNavPropertyETTwoPrimOneWithCustomAnnotations.json");
+      assertNotNull(entity);
+	  List<Annotation> annotations = entity.getNavigationLink("NavPropertyETTwoPrimOne").getAnnotations();
+	  assertEquals(1, annotations.size());
+	  assertEquals("custom.annotation", annotations.get(0).getTerm());
+	  assertEquals("customValue", annotations.get(0).getValue());
+	  assertEquals(ValueType.PRIMITIVE, annotations.get(0).getValueType());
   }
 
   @Test
   public void esAllPrimExpandedToManyWithCustomAnnotations() throws Exception {
-    try {
-      deserialize("EntityESAllPrimExpandedNavPropertyETTwoPrimManyWithCustomAnnotations.json");
-      fail("Expected exception not thrown.");
-    } catch (final DeserializerException e) {
-      assertEquals(DeserializerException.MessageKeys.NOT_IMPLEMENTED, e.getMessageKey());
-    }
+	  Entity entity = deserialize("EntityESAllPrimExpandedNavPropertyETTwoPrimManyWithCustomAnnotations.json");
+	  assertNotNull(entity);
+	  List<Annotation> annotations = entity.getNavigationLink("NavPropertyETTwoPrimMany").getAnnotations();
+	  assertEquals(1, annotations.size());
+	  assertEquals("custom.annotation", annotations.get(0).getTerm());
+	  assertEquals("customValue", annotations.get(0).getValue());
+	  assertEquals(ValueType.PRIMITIVE, annotations.get(0).getValueType());
   }
 
   @Test
diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/deserializer/json/ODataJsonDeserializerEntityTest.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/deserializer/json/ODataJsonDeserializerEntityTest.java
index 25ce502..9de9b10 100644
--- a/lib/server-test/src/test/java/org/apache/olingo/server/core/deserializer/json/ODataJsonDeserializerEntityTest.java
+++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/deserializer/json/ODataJsonDeserializerEntityTest.java
@@ -37,6 +37,7 @@
 import java.util.List;
 
 import org.apache.olingo.commons.api.Constants;
+import org.apache.olingo.commons.api.data.Annotation;
 import org.apache.olingo.commons.api.data.ComplexValue;
 import org.apache.olingo.commons.api.data.Entity;
 import org.apache.olingo.commons.api.data.Link;
@@ -1269,8 +1270,13 @@
         + "{\"PropertyInt16\":123,\"PropertyString\":\"TEST 1\"},"
         + "{\"PropertyInt16\":456,\"PropertyString\":\"TEST 2\"},"
         + "{\"PropertyInt16\":789,\"PropertyString\":\"TEST 3\"}]}";
-    expectException(entityString, "ETMixPrimCollComp",
-        DeserializerException.MessageKeys.NOT_IMPLEMENTED);
+    Entity entity = deserialize(entityString, "ETMixPrimCollComp", ContentType.APPLICATION_JSON);
+    assertNotNull(entity);
+    List<Annotation> annotations = entity.getProperty("CollPropertyString").getAnnotations();
+    assertEquals(1, annotations.size());
+    assertEquals("custom.annotation", annotations.get(0).getTerm());
+    assertEquals(12, annotations.get(0).getValue());
+    assertEquals(ValueType.PRIMITIVE, annotations.get(0).getValueType());
   }
 
   @Test
diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/deserializer/json/ODataJsonDeserializerWithInstanceAnnotationsTest.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/deserializer/json/ODataJsonDeserializerWithInstanceAnnotationsTest.java
new file mode 100644
index 0000000..ab4b2eb
--- /dev/null
+++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/deserializer/json/ODataJsonDeserializerWithInstanceAnnotationsTest.java
@@ -0,0 +1,271 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.olingo.server.core.deserializer.json;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.olingo.commons.api.data.Annotation;
+import org.apache.olingo.commons.api.data.ComplexValue;
+import org.apache.olingo.commons.api.data.Entity;
+import org.apache.olingo.commons.api.data.Property;
+import org.apache.olingo.commons.api.data.ValueType;
+import org.apache.olingo.commons.api.edm.EdmEntityType;
+import org.apache.olingo.commons.api.edm.FullQualifiedName;
+import org.apache.olingo.commons.api.format.ContentType;
+import org.apache.olingo.server.api.OData;
+import org.apache.olingo.server.api.deserializer.DeserializerException;
+import org.apache.olingo.server.api.deserializer.DeserializerResult;
+import org.apache.olingo.server.core.deserializer.AbstractODataDeserializerTest;
+import org.junit.Test;
+
+public class ODataJsonDeserializerWithInstanceAnnotationsTest extends AbstractODataDeserializerTest {
+
+  private static final ContentType CONTENT_TYPE_JSON_IEEE754Compatible =
+      ContentType.create(ContentType.JSON, ContentType.PARAMETER_IEEE754_COMPATIBLE, "true");
+  private static final OData odata = OData.newInstance();
+
+  @Test
+  public void instanceAnnotOnEntity() throws Exception {
+	  final String entityString = "{"
+		        + "\"@context\":\"$metadata#ESAllPrim/$entity\","
+		        + "\"@metadataEtag\":\"W/\\\"metadataETag\\\"\","
+		        + "\"@com.contoso.display.highlight\":true,"
+		        + "\"@com.contoso.PersonalInfo.PhoneNumbers\":"
+		        + "[\"(203)555-1718\",\"(203)555-1719\"],"
+		        + "\"PropertyInt16\":32767,"
+		        + "\"NavPropertyETTwoPrimOne@bind\": \"ETTwoPrim(1)\","
+		        + "\"PropertyString\":\"First Resource - positive values\","
+		        + "\"PropertyBoolean\":true,"
+		        + "\"PropertyByte\":255,"
+		        + "\"PropertySByte\":127,"
+		        + "\"PropertyInt32\":2147483647,"
+		        + "\"PropertyInt64\":9223372036854775807,"
+		        + "\"PropertySingle\":1.79E20,"
+		        + "\"PropertyDouble\":-1.79E19,"
+		        + "\"PropertyDecimal\":34,"
+		        + "\"PropertyBinary\":\"ASNFZ4mrze8=\","
+		        + "\"PropertyDate\":\"2012-12-03\","
+		        + "\"PropertyDateTimeOffset\":\"2012-12-03T07:16:23Z\","
+		        + "\"PropertyDuration\":\"PT6S\","
+		        + "\"PropertyGuid\":\"01234567-89ab-cdef-0123-456789abcdef\","
+		        + "\"PropertyTimeOfDay\":\"03:26:05\""
+		        + "}";
+  final Entity entity = deserializeWithResultWithConstantV401(
+  		new ByteArrayInputStream(entityString.getBytes()), 
+  		"ETAllPrim", ContentType.APPLICATION_JSON).getEntity();
+    assertNotNull(entity);
+    List<Annotation> annotations = entity.getAnnotations();
+    assertEquals(2, annotations.size());
+    assertEquals("com.contoso.display.highlight", annotations.get(0).getTerm());
+    assertTrue((Boolean)annotations.get(0).getValue());
+    assertEquals(ValueType.PRIMITIVE, annotations.get(0).getValueType());
+    assertEquals("com.contoso.PersonalInfo.PhoneNumbers", annotations.get(1).getTerm());
+    assertEquals(ValueType.COLLECTION_PRIMITIVE, annotations.get(1).getValueType());
+    assertEquals(2, annotations.get(1).asCollection().size());
+    assertEquals(1, entity.getNavigationBindings().size());
+    assertEquals("NavPropertyETTwoPrimOne", entity
+    		.getNavigationBinding("NavPropertyETTwoPrimOne").getTitle());
+  }
+  
+  @Test
+  public void instanceAnnotOnEntityProperty() throws Exception {
+	  final String entityString = "{"
+				+	"\"@odata.context\":\"$metadata#ESAllPrim/$entity\","
+				+	"\"@odata.metadataEtag\":\"W/\\\"metadataETag\\\"\","
+				+	"\"@odata.type\":\"#olingo.odata.test1.ETAllPrim\","
+				+	"\"@odata.id\":\"ESAllPrim(32767)\","
+				+	"\"PropertyInt16@odata.type\":\"#Int16\","
+				+	"\"PropertyInt16\":32767,"
+				+	"\"PropertyString@com.contoso.display.style\":{"
+				+		"\"@odata.type\":\"#com.contoso.display.styleType\","
+				+		"\"title@odata.type\":\"#Boolean\","
+				+		"\"title\":true,"
+				+		"\"Order@odata.type\":\"#Int16\","
+				+		"\"Order\":1"
+				+	"},"
+				+	"\"PropertyString\":\"First Resource - positive values\","
+				+	"\"PropertyBoolean\":true,"
+				+	"\"PropertyByte@odata.type\":\"#Byte\","
+				+	"\"PropertyByte\":255,"
+				+	"\"PropertySByte@odata.type\":\"#SByte\","
+				+	"\"PropertySByte\":127,"
+				+	"\"PropertyInt32@odata.type\":\"#Int32\","
+				+	"\"PropertyInt32\":2147483647,"
+				+	"\"PropertyInt64@odata.type\":\"#Int64\","
+				+	"\"PropertyInt64\":9223372036854775807,"
+				+	"\"PropertySingle@odata.type\":\"#Single\","
+				+	"\"PropertySingle\":1.79E20,"
+				+	"\"PropertyDouble\":-1.79E19,"
+				+	"\"PropertyDecimal@odata.type\":\"#Decimal\","
+				+	"\"PropertyDecimal\":34,"
+				+	"\"PropertyBinary@odata.type\":\"#Binary\","
+				+	"\"PropertyBinary\":\"ASNFZ4mrze8=\","
+				+	"\"PropertyDate@odata.type\":\"#Date\","
+				+	"\"PropertyDate\":\"2012-12-03\","
+				+	"\"PropertyDateTimeOffset@odata.type\":\"#DateTimeOffset\","
+				+	"\"PropertyDateTimeOffset\":\"2012-12-03T07:16:23Z\","
+				+	"\"PropertyDuration@odata.type\":\"#Duration\","
+				+	"\"PropertyDuration\":\"PT6S\","
+				+	"\"PropertyGuid@odata.type\":\"#Guid\","
+				+	"\"PropertyGuid\":\"01234567-89ab-cdef-0123-456789abcdef\","
+				+	"\"PropertyTimeOfDay@odata.type\":\"#TimeOfDay\","
+				+	"\"PropertyTimeOfDay\":\"03:26:05\","
+				+	"\"NavPropertyETTwoPrimOne@odata.navigationLink\":\"ESTwoPrim(32767)\","
+				+	"\"NavPropertyETTwoPrimMany@odata.navigationLink\":"
+				+ "\"ESAllPrim(32767)/NavPropertyETTwoPrimMany\""
+				+"}";
+    final Entity entity = deserialize(entityString, "ETAllPrim");
+    assertNotNull(entity);
+    Property property = entity.getProperties().get(1);
+    List<Annotation> annotations = property.getAnnotations();
+    assertEquals(1, annotations.size());
+    assertEquals("com.contoso.display.style", annotations.get(0).getTerm());
+    assertEquals(ValueType.COMPLEX, annotations.get(0).getValueType());
+    ComplexValue value = annotations.get(0).asComplex();
+    assertEquals("#com.contoso.display.styleType", value.getTypeName());
+    List<Property> complxProperties = value.getValue();
+    assertEquals(2, complxProperties.size());
+    assertEquals("title", complxProperties.get(0).getName());
+    assertTrue((Boolean)complxProperties.get(0).getValue());
+    assertEquals("Order", complxProperties.get(1).getName());
+    assertEquals(1, complxProperties.get(1).getValue());
+  }
+  
+  @Test
+  public void instanceAnnotOnComplexProperty() throws Exception {
+	  final String entityString = "{"
+	    		+	"\"@odata.context\":\"$metadata#ESMixPrimCollComp/$entity\","
+	    		+	"\"@odata.metadataEtag\":\"W/\\\"metadataETag\\\"\","
+	    		+	"\"@odata.type\":\"#olingo.odata.test1.ETMixPrimCollComp\","
+	    		+	"\"@odata.id\":\"ESMixPrimCollComp(32767)\","
+	    		+	"\"PropertyInt16@odata.type\":\"#Int16\","
+	    		+	"\"PropertyInt16\":32767,"
+	    		+	"\"CollPropertyString@odata.type\":\"#Collection(String)\","
+	    		+	"\"CollPropertyString\":[\"Employee1@company.example\","
+	    		+ "\"Employee2@company.example\",\"Employee3@company.example\"],"
+	    		+	"\"PropertyComp@com.contoso.display.style\":{"
+	    		+		"\"@odata.type\":\"#com.contoso.display.styleType\","
+	    		+		"\"title@odata.type\":\"#Boolean\","
+	    		+		"\"title\":true,"
+	    		+		"\"Order@odata.type\":\"#Collection(Order)\","
+	    		+		"\"Order\":[{"
+	    		+			"\"@odata.type\":\"#com.contoso.display.orderDetails\""
+	    		+		"},{"
+	    		+			"\"@odata.type\":\"#com.contoso.display.orderDetails\","
+	    		+			"\"name@odata.type\":\"#String\","
+	    		+			"\"name\":\"Cars\","
+	    		+			"\"brand@odata.type\":\"#String\","
+	    		+			"\"brand\":\"BMW\""
+	    		+		"}]"
+	    		+	"},"
+	    		+	"\"PropertyComp\":{"
+	    		+		"\"@odata.type\":\"#olingo.odata.test1.CTTwoPrim\","
+	    		+		"\"PropertyInt16@odata.type\":\"#Int16\","
+	    		+		"\"PropertyInt16\":111,"
+	    		+		"\"PropertyString\":\"TEST A\","
+	    		+		"\"NavPropertyETTwoKeyNavOne@odata.navigationLink\":"
+	    		+ "\"ESTwoKeyNav(PropertyInt16=1,PropertyString='1')\""
+	    		+	"},"
+	    		+	"\"CollPropertyComp@odata.type\":\"#Collection(olingo.odata.test1.CTTwoPrim)\","
+	    		+	"\"CollPropertyComp\":[{"
+	    		+		"\"@odata.type\":\"#olingo.odata.test1.CTTwoPrim\","
+	    		+		"\"PropertyInt16@odata.type\":\"#Int16\","
+	    		+		"\"PropertyInt16\":123,"
+	    		+		"\"PropertyString\":\"TEST 1\","
+	    		+		"\"NavPropertyETTwoKeyNavOne@odata.navigationLink\":"
+	    		+ "\"ESTwoKeyNav(PropertyInt16=1,PropertyString='2')\""
+	    		+	"},{"
+	    		+		"\"@odata.type\":\"#olingo.odata.test1.CTTwoPrim\","
+	    		+		"\"PropertyInt16@odata.type\":\"#Int16\","
+	    		+		"\"PropertyInt16\":456,"
+	    		+		"\"PropertyString\":\"TEST 2\","
+	    		+		"\"NavPropertyETTwoKeyNavOne@odata.navigationLink\":"
+	    		+ "\"ESTwoKeyNav(PropertyInt16=1,PropertyString='2')\""
+	    		+	"},{"
+	    		+		"\"@odata.type\":\"#olingo.odata.test1.CTBase\","
+	    		+		"\"PropertyInt16@odata.type\":\"#Int16\","
+	    		+		"\"PropertyInt16\":789,"
+	    		+		"\"PropertyString\":\"TEST 3\","
+	    		+		"\"AdditionalPropString\":\"ADD TEST\","
+	    		+		"\"NavPropertyETTwoKeyNavOne@odata.navigationLink\":"
+	    		+ "\"ESTwoKeyNav(PropertyInt16=1,PropertyString='2')\""
+	    		+	"}]"
+	    		+"}";
+    final Entity entity = deserialize(entityString, "ETMixPrimCollComp");
+    assertNotNull(entity);
+    Property property = entity.getProperties().get(2);
+    List<Annotation> annotations = property.getAnnotations();
+    assertEquals(1, annotations.size());
+    assertEquals("com.contoso.display.style", annotations.get(0).getTerm());
+    assertEquals(ValueType.COMPLEX, annotations.get(0).getValueType());
+    ComplexValue value = annotations.get(0).asComplex();
+    assertEquals("#com.contoso.display.styleType", value.getTypeName());
+    List<Property> complxProperties = value.getValue();
+    assertEquals(2, complxProperties.size());
+    assertEquals("title", complxProperties.get(0).getName());
+    assertTrue((Boolean)complxProperties.get(0).getValue());
+    assertEquals("Order", complxProperties.get(1).getName());
+    assertEquals(ValueType.COLLECTION_COMPLEX, complxProperties.get(1).getValueType());
+    assertEquals(2, complxProperties.get(1).asCollection().size());
+  }
+  
+  protected static DeserializerResult deserializeWithResultWithConstantV401(final InputStream stream, 
+		  final String entityTypeName, final ContentType contentType) 
+				  throws DeserializerException {
+    final EdmEntityType entityType = edm.getEntityType(new FullQualifiedName(NAMESPACE, entityTypeName));
+    List<String> odataVersions = new ArrayList<>();
+    odataVersions.add("4.01");
+    return odata.createDeserializer(contentType, metadata, odataVersions).entity(stream, entityType);
+  }
+  
+  protected static Entity deserialize(final InputStream stream, final String entityTypeName,
+	      final ContentType contentType) throws DeserializerException {
+	    return deserializeWithResult(stream, entityTypeName, contentType).getEntity();
+	  }
+
+  protected static DeserializerResult deserializeWithResult(final InputStream stream, 
+		  final String entityTypeName, final ContentType contentType) 
+				  throws DeserializerException {
+    final EdmEntityType entityType = edm.getEntityType(new FullQualifiedName(NAMESPACE, entityTypeName));
+    return deserializeWithResult(stream, entityType, contentType);
+  }
+
+  protected static DeserializerResult deserializeWithResult(final InputStream stream, 
+		  final EdmEntityType entityType, final ContentType contentType) 
+				  throws DeserializerException {
+    return odata.createDeserializer(contentType, metadata).entity(stream, entityType);
+  }
+
+  private static Entity deserialize(final String entityString, final String entityTypeName,
+      final ContentType contentType) throws DeserializerException {
+    return deserialize(new ByteArrayInputStream(entityString.getBytes()), entityTypeName, contentType);
+  }
+
+  protected static Entity deserialize(final String entityString, final String entityTypeName)
+      throws DeserializerException {
+    return deserialize(entityString, entityTypeName, ContentType.JSON);
+  }
+}
diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerWithInstanceAnnotationsTest.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerWithInstanceAnnotationsTest.java
new file mode 100644
index 0000000..11c650a
--- /dev/null
+++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerWithInstanceAnnotationsTest.java
@@ -0,0 +1,285 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.olingo.server.core.serializer.json;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.olingo.commons.api.data.Annotation;
+import org.apache.olingo.commons.api.data.ComplexValue;
+import org.apache.olingo.commons.api.data.ContextURL;
+import org.apache.olingo.commons.api.data.ContextURL.Suffix;
+import org.apache.olingo.commons.api.data.Entity;
+import org.apache.olingo.commons.api.data.Property;
+import org.apache.olingo.commons.api.data.ValueType;
+import org.apache.olingo.commons.api.edm.EdmEntityContainer;
+import org.apache.olingo.commons.api.edm.EdmEntitySet;
+import org.apache.olingo.commons.api.edmx.EdmxReference;
+import org.apache.olingo.commons.api.format.ContentType;
+import org.apache.olingo.server.api.OData;
+import org.apache.olingo.server.api.ServiceMetadata;
+import org.apache.olingo.server.api.serializer.EntitySerializerOptions;
+import org.apache.olingo.server.api.serializer.ODataSerializer;
+import org.apache.olingo.server.api.uri.UriHelper;
+import org.apache.olingo.server.tecsvc.MetadataETagSupport;
+import org.apache.olingo.server.tecsvc.data.DataProvider;
+import org.apache.olingo.server.tecsvc.provider.EdmTechProvider;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class ODataJsonSerializerWithInstanceAnnotationsTest {
+  private static final OData odata = OData.newInstance();
+  private static final ServiceMetadata metadata = odata.createServiceMetadata(
+      new EdmTechProvider(), Collections.<EdmxReference> emptyList(), new MetadataETagSupport("W/\"metadataETag\""));
+  private static final EdmEntityContainer entityContainer = metadata.getEdm().getEntityContainer();
+  private final DataProvider data = new DataProvider(odata, metadata.getEdm());
+  private final ODataSerializer serializer = new ODataJsonSerializer(ContentType.JSON);
+  private final ODataSerializer serializerNoMetadata = new ODataJsonSerializer(ContentType.JSON_NO_METADATA);
+  private final ODataSerializer serializerFullMetadata = new ODataJsonSerializer(ContentType.JSON_FULL_METADATA);
+  private final ODataSerializer serializerIEEECompatible =
+      new ODataJsonSerializer(ContentType.create(ContentType.JSON, ContentType.PARAMETER_IEEE754_COMPATIBLE, "true"));
+  private final UriHelper helper = odata.createUriHelper();
+  
+  @Test
+  public void entityWithInstanceAnnotations() throws Exception {
+    final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESAllPrim");
+    final Entity entity = data.readAll(edmEntitySet).getEntities().get(0);
+    Annotation annotation = new Annotation();
+    annotation.setTerm("com.contoso.display.highlight");
+    annotation.setType("Boolean");
+    annotation.setValue(ValueType.PRIMITIVE, true);
+    entity.getAnnotations().add(annotation);
+    annotation = new Annotation();
+    annotation.setTerm("com.contoso.PersonalInfo.PhoneNumbers");
+    annotation.setType("String");
+    annotation.setValue(ValueType.COLLECTION_PRIMITIVE, Arrays.asList("(203)555-1718", "(203)555-1719"));
+    entity.getAnnotations().add(annotation);
+    InputStream result = serializer.entity(metadata, edmEntitySet.getEntityType(), entity,
+        EntitySerializerOptions.with()
+            .contextURL(ContextURL.with().entitySet(edmEntitySet).suffix(Suffix.ENTITY).build())
+            .build()).getContent();
+    final String resultString = IOUtils.toString(result);
+    final String expectedResult = "{"
+        + "\"@odata.context\":\"$metadata#ESAllPrim/$entity\","
+        + "\"@odata.metadataEtag\":\"W/\\\"metadataETag\\\"\","
+        + "\"@com.contoso.display.highlight\":true,"
+        + "\"@com.contoso.PersonalInfo.PhoneNumbers\":"
+        + "[\"(203)555-1718\",\"(203)555-1719\"],"
+        + "\"PropertyInt16\":32767,"
+        + "\"PropertyString\":\"First Resource - positive values\","
+        + "\"PropertyBoolean\":true,"
+        + "\"PropertyByte\":255,"
+        + "\"PropertySByte\":127,"
+        + "\"PropertyInt32\":2147483647,"
+        + "\"PropertyInt64\":9223372036854775807,"
+        + "\"PropertySingle\":1.79E20,"
+        + "\"PropertyDouble\":-1.79E19,"
+        + "\"PropertyDecimal\":34,"
+        + "\"PropertyBinary\":\"ASNFZ4mrze8=\","
+        + "\"PropertyDate\":\"2012-12-03\","
+        + "\"PropertyDateTimeOffset\":\"2012-12-03T07:16:23Z\","
+        + "\"PropertyDuration\":\"PT6S\","
+        + "\"PropertyGuid\":\"01234567-89ab-cdef-0123-456789abcdef\","
+        + "\"PropertyTimeOfDay\":\"03:26:05\""
+        + "}";
+    Assert.assertEquals(expectedResult, resultString);
+  }
+  
+  @Test
+  public void entityPropertyWithInstanceAnnotations() throws Exception {
+    final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESAllPrim");
+    final Entity entity = data.readAll(edmEntitySet).getEntities().get(0);
+    Annotation annotation = new Annotation();
+    annotation.setTerm("com.contoso.display.style");
+    annotation.setType("com.contoso.display.styleType");
+    List<Property> properties = new ArrayList<>();
+    properties.add(new Property("Boolean", "title", ValueType.PRIMITIVE, true));
+    properties.add(new Property("Int16", "Order", ValueType.PRIMITIVE, 1));
+    ComplexValue complexValue = new ComplexValue();
+    complexValue.setTypeName("com.contoso.display.styleType");
+    complexValue.getValue().addAll(properties);
+    annotation.setValue(ValueType.COMPLEX, complexValue);
+    
+    Property property = entity.getProperty("PropertyString");
+    property.getAnnotations().add(annotation);
+    
+    InputStream result = serializerFullMetadata.entity(metadata, edmEntitySet.getEntityType(), entity,
+        EntitySerializerOptions.with()
+            .contextURL(ContextURL.with().entitySet(edmEntitySet).suffix(Suffix.ENTITY).build())
+            .build()).getContent();
+    final String resultString = IOUtils.toString(result);
+    final String expectedResult = "{"
+	+	"\"@odata.context\":\"$metadata#ESAllPrim/$entity\","
+	+	"\"@odata.metadataEtag\":\"W/\\\"metadataETag\\\"\","
+	+	"\"@odata.type\":\"#olingo.odata.test1.ETAllPrim\","
+	+	"\"@odata.id\":\"ESAllPrim(32767)\","
+	+	"\"PropertyInt16@odata.type\":\"#Int16\","
+	+	"\"PropertyInt16\":32767,"
+	+	"\"PropertyString@com.contoso.display.style\":{"
+	+		"\"@odata.type\":\"#com.contoso.display.styleType\","
+	+		"\"title@odata.type\":\"#Boolean\","
+	+		"\"title\":true,"
+	+		"\"Order@odata.type\":\"#Int16\","
+	+		"\"Order\":1"
+	+	"},"
+	+	"\"PropertyString\":\"First Resource - positive values\","
+	+	"\"PropertyBoolean\":true,"
+	+	"\"PropertyByte@odata.type\":\"#Byte\","
+	+	"\"PropertyByte\":255,"
+	+	"\"PropertySByte@odata.type\":\"#SByte\","
+	+	"\"PropertySByte\":127,"
+	+	"\"PropertyInt32@odata.type\":\"#Int32\","
+	+	"\"PropertyInt32\":2147483647,"
+	+	"\"PropertyInt64@odata.type\":\"#Int64\","
+	+	"\"PropertyInt64\":9223372036854775807,"
+	+	"\"PropertySingle@odata.type\":\"#Single\","
+	+	"\"PropertySingle\":1.79E20,"
+	+	"\"PropertyDouble\":-1.79E19,"
+	+	"\"PropertyDecimal@odata.type\":\"#Decimal\","
+	+	"\"PropertyDecimal\":34,"
+	+	"\"PropertyBinary@odata.type\":\"#Binary\","
+	+	"\"PropertyBinary\":\"ASNFZ4mrze8=\","
+	+	"\"PropertyDate@odata.type\":\"#Date\","
+	+	"\"PropertyDate\":\"2012-12-03\","
+	+	"\"PropertyDateTimeOffset@odata.type\":\"#DateTimeOffset\","
+	+	"\"PropertyDateTimeOffset\":\"2012-12-03T07:16:23Z\","
+	+	"\"PropertyDuration@odata.type\":\"#Duration\","
+	+	"\"PropertyDuration\":\"PT6S\","
+	+	"\"PropertyGuid@odata.type\":\"#Guid\","
+	+	"\"PropertyGuid\":\"01234567-89ab-cdef-0123-456789abcdef\","
+	+	"\"PropertyTimeOfDay@odata.type\":\"#TimeOfDay\","
+	+	"\"PropertyTimeOfDay\":\"03:26:05\","
+	+	"\"NavPropertyETTwoPrimOne@odata.navigationLink\":\"ESTwoPrim(32767)\","
+	+	"\"NavPropertyETTwoPrimMany@odata.navigationLink\":\"ESAllPrim(32767)/NavPropertyETTwoPrimMany\","
+	+	"\"#olingo.odata.test1.BAETAllPrimRT\":{"
+	+		"\"title\":\"olingo.odata.test1.BAETAllPrimRT\","
+	+		"\"target\":\"ESAllPrim(32767)/olingo.odata.test1.BAETAllPrimRT\""
+	+	"}"
+	+"}";
+    Assert.assertEquals(expectedResult, resultString);
+  }
+  
+  @Test
+  public void entityComplexPropertyWithInstanceAnnotations() throws Exception {
+    final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESMixPrimCollComp");
+    final Entity entity = data.readAll(edmEntitySet).getEntities().get(0);
+    Annotation annotation = new Annotation();
+    annotation.setTerm("com.contoso.display.style");
+    annotation.setType("com.contoso.display.styleType");
+    List<Property> properties = new ArrayList<>();
+    properties.add(new Property("Boolean", "title", ValueType.PRIMITIVE, true));
+    
+    List<ComplexValue> complexValues = new ArrayList<>();
+    ComplexValue orderComplexValue = new ComplexValue();
+    orderComplexValue.setTypeName("com.contoso.display.orderDetails");
+    complexValues.add(orderComplexValue);
+    
+    orderComplexValue = new ComplexValue();
+    orderComplexValue.setTypeName("com.contoso.display.orderDetails");
+    List<Property> orderProperties = new ArrayList<>();
+    orderProperties.add(new Property("String", "name", ValueType.PRIMITIVE, "Cars"));
+    orderProperties.add(new Property("String", "brand", ValueType.PRIMITIVE, "BMW"));
+    orderComplexValue.getValue().addAll(orderProperties);
+    complexValues.add(orderComplexValue);
+    properties.add(new Property("Order", "Order", ValueType.COLLECTION_COMPLEX, complexValues));
+    
+    
+    ComplexValue complexValue = new ComplexValue();
+    complexValue.setTypeName("com.contoso.display.styleType");
+    complexValue.getValue().addAll(properties);
+    annotation.setValue(ValueType.COMPLEX, complexValue);
+    
+    Property property = entity.getProperty("PropertyComp");
+    property.getAnnotations().add(annotation);
+    
+    InputStream result = serializerFullMetadata.entity(metadata, edmEntitySet.getEntityType(), entity,
+        EntitySerializerOptions.with()
+            .contextURL(ContextURL.with().entitySet(edmEntitySet).suffix(Suffix.ENTITY).build())
+            .build()).getContent();
+    final String resultString = IOUtils.toString(result);
+    final String expectedResult = "{"
+    		+	"\"@odata.context\":\"$metadata#ESMixPrimCollComp/$entity\","
+    		+	"\"@odata.metadataEtag\":\"W/\\\"metadataETag\\\"\","
+    		+	"\"@odata.type\":\"#olingo.odata.test1.ETMixPrimCollComp\","
+    		+	"\"@odata.id\":\"ESMixPrimCollComp(32767)\","
+    		+	"\"PropertyInt16@odata.type\":\"#Int16\","
+    		+	"\"PropertyInt16\":32767,"
+    		+	"\"CollPropertyString@odata.type\":\"#Collection(String)\","
+    		+	"\"CollPropertyString\":[\"Employee1@company.example\","
+    		+ "\"Employee2@company.example\",\"Employee3@company.example\"],"
+    		+	"\"PropertyComp@com.contoso.display.style\":{"
+    		+		"\"@odata.type\":\"#com.contoso.display.styleType\","
+    		+		"\"title@odata.type\":\"#Boolean\","
+    		+		"\"title\":true,"
+    		+		"\"Order@odata.type\":\"#Collection(Order)\","
+    		+		"\"Order\":[{"
+    		+			"\"@odata.type\":\"#com.contoso.display.orderDetails\""
+    		+		"},{"
+    		+			"\"@odata.type\":\"#com.contoso.display.orderDetails\","
+    		+			"\"name@odata.type\":\"#String\","
+    		+			"\"name\":\"Cars\","
+    		+			"\"brand@odata.type\":\"#String\","
+    		+			"\"brand\":\"BMW\""
+    		+		"}]"
+    		+	"},"
+    		+	"\"PropertyComp\":{"
+    		+		"\"@odata.type\":\"#olingo.odata.test1.CTTwoPrim\","
+    		+		"\"PropertyInt16@odata.type\":\"#Int16\","
+    		+		"\"PropertyInt16\":111,"
+    		+		"\"PropertyString\":\"TEST A\","
+    		+		"\"NavPropertyETTwoKeyNavOne@odata.navigationLink\":"
+    		+ "\"ESTwoKeyNav(PropertyInt16=1,PropertyString='1')\""
+    		+	"},"
+    		+	"\"CollPropertyComp@odata.type\":\"#Collection(olingo.odata.test1.CTTwoPrim)\","
+    		+	"\"CollPropertyComp\":[{"
+    		+		"\"@odata.type\":\"#olingo.odata.test1.CTTwoPrim\","
+    		+		"\"PropertyInt16@odata.type\":\"#Int16\","
+    		+		"\"PropertyInt16\":123,"
+    		+		"\"PropertyString\":\"TEST 1\","
+    		+		"\"NavPropertyETTwoKeyNavOne@odata.navigationLink\":"
+    		+ "\"ESTwoKeyNav(PropertyInt16=1,PropertyString='2')\""
+    		+	"},{"
+    		+		"\"@odata.type\":\"#olingo.odata.test1.CTTwoPrim\","
+    		+		"\"PropertyInt16@odata.type\":\"#Int16\","
+    		+		"\"PropertyInt16\":456,"
+    		+		"\"PropertyString\":\"TEST 2\","
+    		+		"\"NavPropertyETTwoKeyNavOne@odata.navigationLink\":"
+    		+ "\"ESTwoKeyNav(PropertyInt16=1,PropertyString='2')\""
+    		+	"},{"
+    		+		"\"@odata.type\":\"#olingo.odata.test1.CTBase\","
+    		+		"\"PropertyInt16@odata.type\":\"#Int16\","
+    		+		"\"PropertyInt16\":789,"
+    		+		"\"PropertyString\":\"TEST 3\","
+    		+		"\"AdditionalPropString\":\"ADD TEST\","
+    		+		"\"NavPropertyETTwoKeyNavOne@odata.navigationLink\":"
+    		+ "\"ESTwoKeyNav(PropertyInt16=1,PropertyString='2')\""
+    		+	"}],"
+    		+	"\"#olingo.odata.test1.BAETMixPrimCollCompRTCTTwoPrim\":{"
+    		+		"\"title\":\"olingo.odata.test1.BAETMixPrimCollCompRTCTTwoPrim\","
+    		+		"\"target\":"
+    		+ "\"ESMixPrimCollComp(32767)/olingo.odata.test1.BAETMixPrimCollCompRTCTTwoPrim\""
+    		+	"}"
+    		+"}";
+    Assert.assertEquals(expectedResult, resultString);
+  }
+}