You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by sk...@apache.org on 2014/04/04 07:26:47 UTC

[43/51] [abbrv] [OLINGO-230] Implementation completed

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/16d3b028/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AtomPropertyDeserializer.java
----------------------------------------------------------------------
diff --git a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AtomPropertyDeserializer.java b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AtomPropertyDeserializer.java
deleted file mode 100644
index d9106a7..0000000
--- a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AtomPropertyDeserializer.java
+++ /dev/null
@@ -1,240 +0,0 @@
-/*
- * 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.commons.core.data;
-
-import javax.xml.stream.XMLEventReader;
-import javax.xml.stream.XMLStreamException;
-import javax.xml.stream.events.Attribute;
-import javax.xml.stream.events.StartElement;
-import javax.xml.stream.events.XMLEvent;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.olingo.commons.api.Constants;
-import org.apache.olingo.commons.api.data.CollectionValue;
-import org.apache.olingo.commons.api.data.Value;
-import org.apache.olingo.commons.api.domain.ODataPropertyType;
-import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
-import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
-import org.apache.olingo.commons.core.edm.EdmTypeInfo;
-
-class AtomPropertyDeserializer extends AbstractAtomDealer {
-
-  private final AtomGeoValueDeserializer geoDeserializer;
-
-  public AtomPropertyDeserializer(final ODataServiceVersion version) {
-    super(version);
-    this.geoDeserializer = new AtomGeoValueDeserializer();
-  }
-
-  private Value fromPrimitive(final XMLEventReader reader, final StartElement start,
-          final EdmTypeInfo typeInfo) throws XMLStreamException {
-
-    Value value = null;
-
-    boolean foundEndProperty = false;
-    while (reader.hasNext() && !foundEndProperty) {
-      final XMLEvent event = reader.nextEvent();
-
-      if (event.isStartElement() && typeInfo != null && typeInfo.getPrimitiveTypeKind().isGeospatial()) {
-        final EdmPrimitiveTypeKind geoType = EdmPrimitiveTypeKind.valueOfFQN(
-                version, typeInfo.getFullQualifiedName().toString());
-        value = new GeospatialValueImpl(this.geoDeserializer.deserialize(reader, event.asStartElement(), geoType));
-      }
-
-      if (event.isCharacters() && !event.asCharacters().isWhiteSpace()
-              && (typeInfo == null || !typeInfo.getPrimitiveTypeKind().isGeospatial())) {
-
-        value = new PrimitiveValueImpl(event.asCharacters().getData());
-      }
-
-      if (event.isEndElement() && start.getName().equals(event.asEndElement().getName())) {
-        foundEndProperty = true;
-      }
-    }
-
-    return value == null ? new PrimitiveValueImpl(StringUtils.EMPTY) : value;
-  }
-
-  private Value fromComplexOrEnum(final XMLEventReader reader, final StartElement start)
-          throws XMLStreamException {
-
-    Value value = null;
-
-    boolean foundEndProperty = false;
-    while (reader.hasNext() && !foundEndProperty) {
-      final XMLEvent event = reader.nextEvent();
-
-      if (event.isStartElement()) {
-        if (value == null) {
-          value = new ComplexValueImpl();
-        }
-        value.asComplex().get().add(deserialize(reader, event.asStartElement()));
-      }
-
-      if (event.isCharacters() && !event.asCharacters().isWhiteSpace()) {
-        value = new EnumValueImpl(event.asCharacters().getData());
-      }
-
-      if (event.isEndElement() && start.getName().equals(event.asEndElement().getName())) {
-        foundEndProperty = true;
-      }
-    }
-
-    return value;
-  }
-
-  private CollectionValue fromCollection(final XMLEventReader reader, final StartElement start,
-          final EdmTypeInfo typeInfo) throws XMLStreamException {
-
-    final CollectionValueImpl value = new CollectionValueImpl();
-
-    final EdmTypeInfo type = typeInfo == null
-            ? null
-            : new EdmTypeInfo.Builder().setTypeExpression(typeInfo.getFullQualifiedName().toString()).build();
-
-    boolean foundEndProperty = false;
-    while (reader.hasNext() && !foundEndProperty) {
-      final XMLEvent event = reader.nextEvent();
-
-      if (event.isStartElement()) {
-        switch (guessPropertyType(reader, typeInfo)) {
-          case COMPLEX:
-          case ENUM:
-            value.get().add(fromComplexOrEnum(reader, event.asStartElement()));
-            break;
-
-          case PRIMITIVE:
-            value.get().add(fromPrimitive(reader, event.asStartElement(), type));
-            break;
-
-          default:
-          // do not add null or empty values
-        }
-      }
-
-      if (event.isEndElement() && start.getName().equals(event.asEndElement().getName())) {
-        foundEndProperty = true;
-      }
-    }
-
-    return value;
-  }
-
-  private ODataPropertyType guessPropertyType(final XMLEventReader reader, final EdmTypeInfo typeInfo)
-          throws XMLStreamException {
-
-    XMLEvent child = null;
-    while (reader.hasNext() && child == null) {
-      final XMLEvent event = reader.peek();
-      if (event.isCharacters() && event.asCharacters().isWhiteSpace()) {
-        reader.nextEvent();
-      } else {
-        child = event;
-      }
-    }
-
-    final ODataPropertyType type;
-    if (child == null) {
-      type = typeInfo == null || typeInfo.isPrimitiveType()
-              ? ODataPropertyType.PRIMITIVE
-              : ODataPropertyType.ENUM;
-    } else {
-      if (child.isStartElement()) {
-        if (Constants.NS_GML.equals(child.asStartElement().getName().getNamespaceURI())) {
-          type = ODataPropertyType.PRIMITIVE;
-        } else if (elementQName.equals(child.asStartElement().getName())) {
-          type = ODataPropertyType.COLLECTION;
-        } else {
-          type = ODataPropertyType.COMPLEX;
-        }
-      } else if (child.isCharacters()) {
-        type = typeInfo == null || typeInfo.isPrimitiveType()
-                ? ODataPropertyType.PRIMITIVE
-                : ODataPropertyType.ENUM;
-      } else {
-        type = ODataPropertyType.EMPTY;
-      }
-    }
-
-    return type;
-  }
-
-  public AtomPropertyImpl deserialize(final XMLEventReader reader, final StartElement start)
-          throws XMLStreamException {
-
-    final AtomPropertyImpl property = new AtomPropertyImpl();
-
-    if (ODataServiceVersion.V40 == version && v4PropertyValueQName.equals(start.getName())) {
-      // retrieve name from context
-      final Attribute context = start.getAttributeByName(contextQName);
-      if (context != null) {
-        property.setName(StringUtils.substringAfterLast(context.getValue(), "/"));
-      }
-    } else {
-      property.setName(start.getName().getLocalPart());
-    }
-
-    final Attribute nullAttr = start.getAttributeByName(this.nullQName);
-
-    Value value;
-    if (nullAttr == null) {
-      final Attribute typeAttr = start.getAttributeByName(this.typeQName);
-      final String typeAttrValue = typeAttr == null ? null : typeAttr.getValue();
-
-      final EdmTypeInfo typeInfo = StringUtils.isBlank(typeAttrValue)
-              ? null
-              : new EdmTypeInfo.Builder().setTypeExpression(typeAttrValue).build();
-
-      if (typeInfo != null) {
-        property.setType(typeInfo.getTypeExpression());
-      }
-
-      final ODataPropertyType propType = typeInfo == null
-              ? guessPropertyType(reader, typeInfo)
-              : typeInfo.isCollection()
-              ? ODataPropertyType.COLLECTION
-              : typeInfo.isPrimitiveType()
-              ? ODataPropertyType.PRIMITIVE
-              : ODataPropertyType.COMPLEX;
-
-      switch (propType) {
-        case COLLECTION:
-          value = fromCollection(reader, start, typeInfo);
-          break;
-
-        case COMPLEX:
-          value = fromComplexOrEnum(reader, start);
-          break;
-
-        case PRIMITIVE:
-          value = fromPrimitive(reader, start, typeInfo);
-          break;
-
-        case EMPTY:
-        default:
-          value = new PrimitiveValueImpl(StringUtils.EMPTY);
-      }
-    } else {
-      value = new NullValueImpl();
-    }
-
-    property.setValue(value);
-
-    return property;
-  }
-}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/16d3b028/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AtomPropertySerializer.java
----------------------------------------------------------------------
diff --git a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AtomPropertySerializer.java b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AtomPropertySerializer.java
deleted file mode 100644
index 0461ad2..0000000
--- a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AtomPropertySerializer.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * 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.commons.core.data;
-
-import javax.xml.stream.XMLStreamException;
-import javax.xml.stream.XMLStreamWriter;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.olingo.commons.api.Constants;
-import org.apache.olingo.commons.api.data.CollectionValue;
-import org.apache.olingo.commons.api.data.Property;
-import org.apache.olingo.commons.api.data.Value;
-import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
-import org.apache.olingo.commons.core.edm.EdmTypeInfo;
-
-class AtomPropertySerializer extends AbstractAtomDealer {
-
-  private final AtomGeoValueSerializer geoSerializer;
-
-  public AtomPropertySerializer(final ODataServiceVersion version) {
-    super(version);
-    this.geoSerializer = new AtomGeoValueSerializer();
-  }
-
-  private void collection(final XMLStreamWriter writer, final CollectionValue value) throws XMLStreamException {
-    for (Value item : value.get()) {
-      if (version == ODataServiceVersion.V30) {
-        writer.writeStartElement(Constants.PREFIX_DATASERVICES, Constants.ELEM_ELEMENT,
-                version.getNamespaceMap().get(ODataServiceVersion.NS_DATASERVICES));
-      } else {
-        writer.writeStartElement(Constants.PREFIX_METADATA, Constants.ELEM_ELEMENT,
-                version.getNamespaceMap().get(ODataServiceVersion.NS_METADATA));
-      }
-      value(writer, item);
-      writer.writeEndElement();
-    }
-  }
-
-  private void value(final XMLStreamWriter writer, final Value value) throws XMLStreamException {
-    if (value.isPrimitive()) {
-      writer.writeCharacters(value.asPrimitive().get());
-    } else if (value.isEnum()) {
-      writer.writeCharacters(value.asEnum().get());
-    } else if (value.isGeospatial()) {
-      this.geoSerializer.serialize(writer, value.asGeospatial().get());
-    } else if (value.isCollection()) {
-      collection(writer, value.asCollection());
-    } else if (value.isComplex()) {
-      for (Property property : value.asComplex().get()) {
-        property(writer, property, false);
-      }
-    }
-  }
-
-  public void property(final XMLStreamWriter writer, final Property property, final boolean standalone)
-          throws XMLStreamException {
-
-    if (version == ODataServiceVersion.V40 && standalone) {
-      writer.writeStartElement(Constants.PREFIX_METADATA, Constants.VALUE,
-              version.getNamespaceMap().get(ODataServiceVersion.NS_DATASERVICES));
-    } else {
-      writer.writeStartElement(Constants.PREFIX_DATASERVICES, property.getName(),
-              version.getNamespaceMap().get(ODataServiceVersion.NS_DATASERVICES));
-    }
-
-    if (standalone) {
-      namespaces(writer);
-    }
-
-    if (StringUtils.isNotBlank(property.getType())) {
-      String type = property.getType();
-      if (version == ODataServiceVersion.V40) {
-        final EdmTypeInfo typeInfo = new EdmTypeInfo.Builder().setTypeExpression(property.getType()).build();
-        if (typeInfo.isPrimitiveType()) {
-          if (typeInfo.isCollection()) {
-            type = "#Collection(" + typeInfo.getFullQualifiedName().getName() + ")";
-          } else {
-            type = typeInfo.getFullQualifiedName().getName();
-          }
-        } else {
-          type = "#" + property.getType();
-        }
-      }
-      writer.writeAttribute(Constants.PREFIX_METADATA, version.getNamespaceMap().get(ODataServiceVersion.NS_METADATA),
-              Constants.ATTR_TYPE, type);
-    }
-
-    if (property.getValue().isNull()) {
-      writer.writeAttribute(Constants.PREFIX_METADATA, version.getNamespaceMap().get(ODataServiceVersion.NS_METADATA),
-              Constants.ATTR_NULL, Boolean.TRUE.toString());
-    } else {
-      value(writer, property.getValue());
-    }
-
-    writer.writeEndElement();
-  }
-
-  public void property(final XMLStreamWriter writer, final Property property) throws XMLStreamException {
-    property(writer, property, true);
-  }
-}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/16d3b028/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AtomSerializer.java
----------------------------------------------------------------------
diff --git a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AtomSerializer.java b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AtomSerializer.java
index f89aaee..d0e8587 100644
--- a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AtomSerializer.java
+++ b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AtomSerializer.java
@@ -27,22 +27,106 @@ import javax.xml.stream.XMLStreamException;
 import javax.xml.stream.XMLStreamWriter;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.olingo.commons.api.Constants;
+import org.apache.olingo.commons.api.data.CollectionValue;
 import org.apache.olingo.commons.api.data.Entry;
 import org.apache.olingo.commons.api.data.Feed;
 import org.apache.olingo.commons.api.data.Link;
 import org.apache.olingo.commons.api.data.Property;
+import org.apache.olingo.commons.api.data.Value;
 import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
 import org.apache.olingo.commons.api.format.ContentType;
+import org.apache.olingo.commons.core.edm.EdmTypeInfo;
 
 public class AtomSerializer extends AbstractAtomDealer {
 
   private static final XMLOutputFactory FACTORY = XMLOutputFactory.newInstance();
 
-  private final AtomPropertySerializer propSerializer;
+  private final AtomGeoValueSerializer geoSerializer;
 
   public AtomSerializer(final ODataServiceVersion version) {
     super(version);
-    this.propSerializer = new AtomPropertySerializer(version);
+    this.geoSerializer = new AtomGeoValueSerializer();
+  }
+
+  private void collection(final XMLStreamWriter writer, final CollectionValue value) throws XMLStreamException {
+    for (Value item : value.get()) {
+      if (version.compareTo(ODataServiceVersion.V40) < 0) {
+        writer.writeStartElement(Constants.PREFIX_DATASERVICES, Constants.ELEM_ELEMENT,
+                version.getNamespaceMap().get(ODataServiceVersion.NS_DATASERVICES));
+      } else {
+        writer.writeStartElement(Constants.PREFIX_METADATA, Constants.ELEM_ELEMENT,
+                version.getNamespaceMap().get(ODataServiceVersion.NS_METADATA));
+      }
+      value(writer, item);
+      writer.writeEndElement();
+    }
+  }
+
+  private void value(final XMLStreamWriter writer, final Value value) throws XMLStreamException {
+    if (value.isPrimitive()) {
+      writer.writeCharacters(value.asPrimitive().get());
+    } else if (value.isEnum()) {
+      writer.writeCharacters(value.asEnum().get());
+    } else if (value.isGeospatial()) {
+      this.geoSerializer.serialize(writer, value.asGeospatial().get());
+    } else if (value.isCollection()) {
+      collection(writer, value.asCollection());
+    } else if (value.isComplex()) {
+      for (Property property : value.asComplex().get()) {
+        property(writer, property, false);
+      }
+    }
+  }
+
+  public void property(final XMLStreamWriter writer, final Property property, final boolean standalone)
+          throws XMLStreamException {
+
+    if (version.compareTo(ODataServiceVersion.V40) >= 0 && standalone) {
+      writer.writeStartElement(Constants.PREFIX_METADATA, Constants.VALUE,
+              version.getNamespaceMap().get(ODataServiceVersion.NS_DATASERVICES));
+    } else {
+      writer.writeStartElement(Constants.PREFIX_DATASERVICES, property.getName(),
+              version.getNamespaceMap().get(ODataServiceVersion.NS_DATASERVICES));
+    }
+
+    if (standalone) {
+      namespaces(writer);
+    }
+
+    if (StringUtils.isNotBlank(property.getType())) {
+      String type = property.getType();
+      if (version.compareTo(ODataServiceVersion.V40) >= 0) {
+        final EdmTypeInfo typeInfo = new EdmTypeInfo.Builder().setTypeExpression(property.getType()).build();
+        if (typeInfo.isPrimitiveType()) {
+          if (typeInfo.isCollection()) {
+            type = "#Collection(" + typeInfo.getFullQualifiedName().getName() + ")";
+          } else {
+            type = typeInfo.getFullQualifiedName().getName();
+          }
+        } else {
+          type = "#" + property.getType();
+        }
+      }
+      writer.writeAttribute(Constants.PREFIX_METADATA, version.getNamespaceMap().get(ODataServiceVersion.NS_METADATA),
+              Constants.ATTR_TYPE, type);
+    }
+
+    if (property.getValue().isNull()) {
+      writer.writeAttribute(Constants.PREFIX_METADATA, version.getNamespaceMap().get(ODataServiceVersion.NS_METADATA),
+              Constants.ATTR_NULL, Boolean.TRUE.toString());
+    } else {
+      value(writer, property.getValue());
+      if (property.getValue().isLinkedComplex()) {
+        links(writer, property.getValue().asLinkedComplex().getAssociationLinks());
+        links(writer, property.getValue().asLinkedComplex().getNavigationLinks());
+      }
+    }
+
+    writer.writeEndElement();
+  }
+
+  private void property(final XMLStreamWriter writer, final Property property) throws XMLStreamException {
+    property(writer, property, true);
   }
 
   private void startDocument(final XMLStreamWriter writer, final String rootElement) throws XMLStreamException {
@@ -59,7 +143,7 @@ public class AtomSerializer extends AbstractAtomDealer {
 
     writer.writeStartDocument();
 
-    propSerializer.property(writer, property);
+    property(writer, property);
 
     writer.writeEndDocument();
     writer.flush();
@@ -122,7 +206,7 @@ public class AtomSerializer extends AbstractAtomDealer {
 
   private void properties(final XMLStreamWriter writer, final List<Property> properties) throws XMLStreamException {
     for (Property property : properties) {
-      propSerializer.property(writer, property, false);
+      property(writer, property, false);
     }
   }
 

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/16d3b028/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONEntryDeserializer.java
----------------------------------------------------------------------
diff --git a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONEntryDeserializer.java b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONEntryDeserializer.java
index 8abd3f3..2c8399d 100644
--- a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONEntryDeserializer.java
+++ b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONEntryDeserializer.java
@@ -21,11 +21,8 @@ package org.apache.olingo.commons.core.data;
 import com.fasterxml.jackson.core.JsonParseException;
 import com.fasterxml.jackson.core.JsonParser;
 import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.core.ObjectCodec;
-import com.fasterxml.jackson.core.type.TypeReference;
 import com.fasterxml.jackson.databind.DeserializationContext;
 import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import java.io.IOException;
 import java.net.URI;
@@ -47,47 +44,11 @@ import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
  */
 public class JSONEntryDeserializer extends AbstractJsonDeserializer<JSONEntryImpl> {
 
-  private String getTitle(final Map.Entry<String, JsonNode> entry) {
-    return entry.getKey().substring(0, entry.getKey().indexOf('@'));
-  }
-
-  private String setInline(final String name, final String suffix, final ObjectNode tree,
-          final ObjectCodec codec, final LinkImpl link) throws IOException {
-
-    final String entryNamePrefix = name.substring(0, name.indexOf(suffix));
-    if (tree.has(entryNamePrefix)) {
-      final JsonNode inline = tree.path(entryNamePrefix);
-
-      if (inline instanceof ObjectNode) {
-        link.setType(ODataLinkType.ENTITY_NAVIGATION.toString());
-
-        link.setInlineEntry(inline.traverse(codec).<Container<JSONEntryImpl>>readValueAs(
-                new TypeReference<JSONEntryImpl>() {
-        }).getObject());
-      }
-
-      if (inline instanceof ArrayNode) {
-        link.setType(ODataLinkType.ENTITY_SET_NAVIGATION.toString());
-
-        final JSONFeedImpl feed = new JSONFeedImpl();
-        final Iterator<JsonNode> entries = ((ArrayNode) inline).elements();
-        while (entries.hasNext()) {
-          feed.getEntries().add(entries.next().traverse(codec).<Container<JSONEntryImpl>>readValuesAs(
-                  new TypeReference<JSONEntryImpl>() {
-          }).next().getObject());
-        }
-
-        link.setInlineFeed(feed);
-      }
-    }
-    return entryNamePrefix;
-  }
-
   @Override
   protected Container<JSONEntryImpl> doDeserialize(final JsonParser parser, final DeserializationContext ctxt)
           throws IOException, JsonProcessingException {
 
-    final ObjectNode tree = (ObjectNode) parser.getCodec().readTree(parser);
+    final ObjectNode tree = parser.getCodec().readTree(parser);
 
     if (tree.has(Constants.VALUE) && tree.get(Constants.VALUE).isArray()) {
       throw new JsonParseException("Expected OData Entity, found EntitySet", parser.getCurrentLocation());
@@ -173,35 +134,8 @@ public class JSONEntryDeserializer extends AbstractJsonDeserializer<JSONEntryImp
     for (final Iterator<Map.Entry<String, JsonNode>> itor = tree.fields(); itor.hasNext();) {
       final Map.Entry<String, JsonNode> field = itor.next();
 
-      if (field.getKey().endsWith(jsonNavigationLink)) {
-        final LinkImpl link = new LinkImpl();
-        link.setTitle(getTitle(field));
-        link.setRel(version.getNamespaceMap().get(ODataServiceVersion.NAVIGATION_LINK_REL) + getTitle(field));
-
-        if (field.getValue().isValueNode()) {
-          link.setHref(field.getValue().textValue());
-          link.setType(ODataLinkType.ENTITY_NAVIGATION.toString());
-        }
-        // NOTE: this should be expected to happen, but it isn't - at least up to OData 4.0
-                /* if (field.getValue().isArray()) {
-         * link.setHref(field.getValue().asText());
-         * link.setType(ODataLinkType.ENTITY_SET_NAVIGATION.toString());
-         * } */
-
-        entry.getNavigationLinks().add(link);
-
-        toRemove.add(field.getKey());
-        toRemove.add(setInline(field.getKey(), jsonNavigationLink, tree, parser.getCodec(), link));
-      } else if (field.getKey().endsWith(jsonAssociationLink)) {
-        final LinkImpl link = new LinkImpl();
-        link.setTitle(getTitle(field));
-        link.setRel(version.getNamespaceMap().get(ODataServiceVersion.ASSOCIATION_LINK_REL) + getTitle(field));
-        link.setHref(field.getValue().textValue());
-        link.setType(ODataLinkType.ASSOCIATION.toString());
-        entry.getAssociationLinks().add(link);
-
-        toRemove.add(field.getKey());
-      } else if (field.getKey().endsWith(getJSONAnnotation(jsonMediaEditLink))) {
+      links(field, entry, toRemove, tree, parser.getCodec());
+      if (field.getKey().endsWith(getJSONAnnotation(jsonMediaEditLink))) {
         final LinkImpl link = new LinkImpl();
         link.setTitle(getTitle(field));
         link.setRel(version.getNamespaceMap().get(ODataServiceVersion.MEDIA_EDIT_LINK_REL) + getTitle(field));
@@ -251,7 +185,7 @@ public class JSONEntryDeserializer extends AbstractJsonDeserializer<JSONEntryImp
         property.setType(type);
         type = null;
 
-        value(property, field.getValue());
+        value(property, field.getValue(), parser.getCodec());
         entry.getProperties().add(property);
       }
     }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/16d3b028/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONEntrySerializer.java
----------------------------------------------------------------------
diff --git a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONEntrySerializer.java b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONEntrySerializer.java
index eb51362..1f4bbea 100644
--- a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONEntrySerializer.java
+++ b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONEntrySerializer.java
@@ -22,16 +22,9 @@ import com.fasterxml.jackson.core.JsonGenerator;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.SerializerProvider;
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.olingo.commons.api.Constants;
 import org.apache.olingo.commons.api.data.Entry;
 import org.apache.olingo.commons.api.data.Link;
 import org.apache.olingo.commons.api.data.Property;
-import org.apache.olingo.commons.api.domain.ODataLinkType;
 import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
 
 /**
@@ -49,49 +42,12 @@ public class JSONEntrySerializer extends AbstractJsonSerializer<JSONEntryImpl> {
       jgen.writeStringField(version.getJSONMap().get(ODataServiceVersion.JSON_ID), entry.getId());
     }
 
-    final Map<String, List<String>> entitySetLinks = new HashMap<String, List<String>>();
-
-    for (Link link : entry.getNavigationLinks()) {
-      ODataLinkType type = null;
-      try {
-        type = ODataLinkType.fromString(version, link.getRel(), link.getType());
-      } catch (IllegalArgumentException e) {
-        // ignore   
-      }
-
-      if (type == ODataLinkType.ENTITY_SET_NAVIGATION) {
-        final List<String> uris;
-        if (entitySetLinks.containsKey(link.getTitle())) {
-          uris = entitySetLinks.get(link.getTitle());
-        } else {
-          uris = new ArrayList<String>();
-          entitySetLinks.put(link.getTitle(), uris);
-        }
-        uris.add(link.getHref());
-      } else {
-        if (StringUtils.isNotBlank(link.getHref())) {
-          jgen.writeStringField(link.getTitle() + Constants.JSON_BIND_LINK_SUFFIX, link.getHref());
-        }
-      }
-
-      if (link.getInlineEntry() != null) {
-        jgen.writeObjectField(link.getTitle(), link.getInlineEntry());
-      } else if (link.getInlineFeed() != null) {
-        jgen.writeArrayFieldStart(link.getTitle());
-        for (Entry subEntry : link.getInlineFeed().getEntries()) {
-          jgen.writeObject(subEntry);
-        }
-        jgen.writeEndArray();
-      }
-    }
-    for (Map.Entry<String, List<String>> entitySetLink : entitySetLinks.entrySet()) {
-      jgen.writeArrayFieldStart(entitySetLink.getKey() + Constants.JSON_BIND_LINK_SUFFIX);
-      for (String uri : entitySetLink.getValue()) {
-        jgen.writeString(uri);
-      }
-      jgen.writeEndArray();
+    for (Property property : entry.getProperties()) {
+      property(jgen, property, property.getName());
     }
 
+    links(entry, jgen);
+
     for (Link link : entry.getMediaEditLinks()) {
       if (link.getTitle() == null) {
         jgen.writeStringField(version.getJSONMap().get(ODataServiceVersion.JSON_MEDIAEDIT_LINK), link.getHref());
@@ -109,10 +65,6 @@ public class JSONEntrySerializer extends AbstractJsonSerializer<JSONEntryImpl> {
       }
     }
 
-    for (Property property : entry.getProperties()) {
-      property(jgen, property, property.getName());
-    }
-
     jgen.writeEndObject();
   }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/16d3b028/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONPropertyDeserializer.java
----------------------------------------------------------------------
diff --git a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONPropertyDeserializer.java b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONPropertyDeserializer.java
index 381b6d4..d640a60 100644
--- a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONPropertyDeserializer.java
+++ b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONPropertyDeserializer.java
@@ -73,7 +73,7 @@ public class JSONPropertyDeserializer extends AbstractJsonDeserializer<JSONPrope
     }
 
     if (property.getValue() == null) {
-      value(property, tree.has(Constants.VALUE) ? tree.get(Constants.VALUE) : tree);
+      value(property, tree.has(Constants.VALUE) ? tree.get(Constants.VALUE) : tree, parser.getCodec());
     }
 
     return new Container<JSONPropertyImpl>(contextURL, metadataETag, property);

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/16d3b028/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/LinkedComplexValueImpl.java
----------------------------------------------------------------------
diff --git a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/LinkedComplexValueImpl.java b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/LinkedComplexValueImpl.java
new file mode 100644
index 0000000..4394dfa
--- /dev/null
+++ b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/LinkedComplexValueImpl.java
@@ -0,0 +1,47 @@
+/*
+ * 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.commons.core.data;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.olingo.commons.api.data.Link;
+import org.apache.olingo.commons.api.data.LinkedComplexValue;
+
+public class LinkedComplexValueImpl extends ComplexValueImpl implements LinkedComplexValue {
+
+  private final List<Link> associationLinks = new ArrayList<Link>();
+
+  private final List<Link> navigationLinks = new ArrayList<Link>();
+
+  @Override
+  public boolean isLinkedComplex() {
+    return true;
+  }
+
+  @Override
+  public List<Link> getAssociationLinks() {
+    return associationLinks;
+  }
+
+  @Override
+  public List<Link> getNavigationLinks() {
+    return navigationLinks;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/16d3b028/lib/commons-core/src/main/java/org/apache/olingo/commons/core/domain/v4/ODataCollectionValueImpl.java
----------------------------------------------------------------------
diff --git a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/domain/v4/ODataCollectionValueImpl.java b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/domain/v4/ODataCollectionValueImpl.java
index b0e0539..f130d90 100644
--- a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/domain/v4/ODataCollectionValueImpl.java
+++ b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/domain/v4/ODataCollectionValueImpl.java
@@ -18,6 +18,7 @@
  */
 package org.apache.olingo.commons.core.domain.v4;
 
+import org.apache.olingo.commons.api.domain.v4.ODataLinkedComplexValue;
 import org.apache.olingo.commons.api.domain.v4.ODataEnumValue;
 import org.apache.olingo.commons.api.domain.v4.ODataValue;
 import org.apache.olingo.commons.core.domain.AbstractODataCollectionValue;
@@ -39,4 +40,14 @@ public class ODataCollectionValueImpl extends AbstractODataCollectionValue<OData
   public ODataEnumValue asEnum() {
     return null;
   }
+
+  @Override
+  public boolean isLinkedComplex() {
+    return false;
+  }
+
+  @Override
+  public ODataLinkedComplexValue asLinkedComplex() {
+    return null;
+  }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/16d3b028/lib/commons-core/src/main/java/org/apache/olingo/commons/core/domain/v4/ODataComplexValueImpl.java
----------------------------------------------------------------------
diff --git a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/domain/v4/ODataComplexValueImpl.java b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/domain/v4/ODataComplexValueImpl.java
index 7c6e72f..711fd53 100644
--- a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/domain/v4/ODataComplexValueImpl.java
+++ b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/domain/v4/ODataComplexValueImpl.java
@@ -18,15 +18,28 @@
  */
 package org.apache.olingo.commons.core.domain.v4;
 
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.olingo.commons.api.domain.ODataLink;
+import org.apache.olingo.commons.api.domain.v4.ODataLinkedComplexValue;
 import org.apache.olingo.commons.api.domain.v4.ODataEnumValue;
 import org.apache.olingo.commons.api.domain.v4.ODataProperty;
-import org.apache.olingo.commons.api.domain.v4.ODataValue;
 import org.apache.olingo.commons.core.domain.AbstractODataComplexValue;
 
-public class ODataComplexValueImpl extends AbstractODataComplexValue<ODataProperty> implements ODataValue {
+public class ODataComplexValueImpl extends AbstractODataComplexValue<ODataProperty> implements ODataLinkedComplexValue {
 
   private static final long serialVersionUID = 1143925901934898802L;
 
+  /**
+   * Navigation links (might contain in-line entities or feeds).
+   */
+  private final List<ODataLink> navigationLinks = new ArrayList<ODataLink>();
+
+  /**
+   * Association links.
+   */
+  private final List<ODataLink> associationLinks = new ArrayList<ODataLink>();
+
   public ODataComplexValueImpl(final String typeName) {
     super(typeName);
   }
@@ -41,4 +54,73 @@ public class ODataComplexValueImpl extends AbstractODataComplexValue<ODataProper
     return null;
   }
 
+  @Override
+  public boolean isLinkedComplex() {
+    return true;
+  }
+
+  @Override
+  public ODataLinkedComplexValue asLinkedComplex() {
+    return this;
+  }
+
+  @Override
+  public boolean addLink(final ODataLink link) {
+    boolean result = false;
+
+    switch (link.getType()) {
+      case ASSOCIATION:
+        result = associationLinks.contains(link) ? false : associationLinks.add(link);
+        break;
+
+      case ENTITY_NAVIGATION:
+      case ENTITY_SET_NAVIGATION:
+        result = navigationLinks.contains(link) ? false : navigationLinks.add(link);
+        break;
+
+      case MEDIA_EDIT:
+        throw new IllegalArgumentException("Complex values cannot have media links!");
+
+      default:
+    }
+
+    return result;
+  }
+
+  @Override
+  public boolean removeLink(final ODataLink link) {
+    return associationLinks.remove(link) || navigationLinks.remove(link);
+  }
+
+  private ODataLink getLink(final List<ODataLink> links, final String name) {
+    ODataLink result = null;
+    for (ODataLink link : links) {
+      if (name.equals(link.getName())) {
+        result = link;
+      }
+    }
+
+    return result;
+  }
+
+  @Override
+  public ODataLink getNavigationLink(final String name) {
+    return getLink(navigationLinks, name);
+  }
+
+  @Override
+  public List<ODataLink> getNavigationLinks() {
+    return navigationLinks;
+  }
+
+  @Override
+  public ODataLink getAssociationLink(final String name) {
+    return getLink(associationLinks, name);
+  }
+
+  @Override
+  public List<ODataLink> getAssociationLinks() {
+    return associationLinks;
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/16d3b028/lib/commons-core/src/main/java/org/apache/olingo/commons/core/domain/v4/ODataEnumValueImpl.java
----------------------------------------------------------------------
diff --git a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/domain/v4/ODataEnumValueImpl.java b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/domain/v4/ODataEnumValueImpl.java
index ee21018..f68fc7f 100644
--- a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/domain/v4/ODataEnumValueImpl.java
+++ b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/domain/v4/ODataEnumValueImpl.java
@@ -19,6 +19,7 @@
 package org.apache.olingo.commons.core.domain.v4;
 
 import org.apache.olingo.commons.api.domain.AbstractODataValue;
+import org.apache.olingo.commons.api.domain.v4.ODataLinkedComplexValue;
 import org.apache.olingo.commons.api.domain.v4.ODataEnumValue;
 
 public class ODataEnumValueImpl extends AbstractODataValue implements ODataEnumValue {
@@ -47,4 +48,13 @@ public class ODataEnumValueImpl extends AbstractODataValue implements ODataEnumV
     return this;
   }
 
+  @Override
+  public boolean isLinkedComplex() {
+    return false;
+  }
+
+  @Override
+  public ODataLinkedComplexValue asLinkedComplex() {
+    return null;
+  }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/16d3b028/lib/commons-core/src/main/java/org/apache/olingo/commons/core/domain/v4/ODataObjectFactoryImpl.java
----------------------------------------------------------------------
diff --git a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/domain/v4/ODataObjectFactoryImpl.java b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/domain/v4/ODataObjectFactoryImpl.java
index 4f46a76..ddb0a53 100644
--- a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/domain/v4/ODataObjectFactoryImpl.java
+++ b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/domain/v4/ODataObjectFactoryImpl.java
@@ -27,6 +27,7 @@ import org.apache.olingo.commons.api.domain.v4.ODataEntitySet;
 import org.apache.olingo.commons.api.domain.v4.ODataObjectFactory;
 import org.apache.olingo.commons.api.domain.v4.ODataEntity;
 import org.apache.olingo.commons.api.domain.v4.ODataEnumValue;
+import org.apache.olingo.commons.api.domain.v4.ODataLinkedComplexValue;
 import org.apache.olingo.commons.api.domain.v4.ODataProperty;
 import org.apache.olingo.commons.api.domain.v4.ODataValue;
 import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
@@ -76,6 +77,11 @@ public class ODataObjectFactoryImpl extends AbstractODataObjectFactory implement
   }
 
   @Override
+  public ODataLinkedComplexValue newLinkedComplexValue(final String typeName) {
+    return new ODataComplexValueImpl(typeName);
+  }
+
+  @Override
   public ODataCollectionValue<ODataValue> newCollectionValue(final String typeName) {
     return new ODataCollectionValueImpl(typeName);
   }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/16d3b028/lib/commons-core/src/main/java/org/apache/olingo/commons/core/domain/v4/ODataPrimitiveValueImpl.java
----------------------------------------------------------------------
diff --git a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/domain/v4/ODataPrimitiveValueImpl.java b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/domain/v4/ODataPrimitiveValueImpl.java
index 0d7ace8..dcf51c9 100644
--- a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/domain/v4/ODataPrimitiveValueImpl.java
+++ b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/domain/v4/ODataPrimitiveValueImpl.java
@@ -18,6 +18,7 @@
  */
 package org.apache.olingo.commons.core.domain.v4;
 
+import org.apache.olingo.commons.api.domain.v4.ODataLinkedComplexValue;
 import org.apache.olingo.commons.api.domain.v4.ODataEnumValue;
 import org.apache.olingo.commons.api.domain.v4.ODataValue;
 import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
@@ -53,4 +54,14 @@ public class ODataPrimitiveValueImpl extends AbstractODataPrimitiveValue impleme
     return null;
   }
 
+  @Override
+  public boolean isLinkedComplex() {
+    return false;
+  }
+
+  @Override
+  public ODataLinkedComplexValue asLinkedComplex() {
+    return null;
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/16d3b028/lib/commons-core/src/main/java/org/apache/olingo/commons/core/domain/v4/ODataPropertyImpl.java
----------------------------------------------------------------------
diff --git a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/domain/v4/ODataPropertyImpl.java b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/domain/v4/ODataPropertyImpl.java
index 85cdf67..34b4cfa 100644
--- a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/domain/v4/ODataPropertyImpl.java
+++ b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/domain/v4/ODataPropertyImpl.java
@@ -21,6 +21,7 @@ package org.apache.olingo.commons.core.domain.v4;
 import org.apache.olingo.commons.api.domain.ODataCollectionValue;
 import org.apache.olingo.commons.api.domain.ODataComplexValue;
 import org.apache.olingo.commons.api.domain.v4.ODataEnumValue;
+import org.apache.olingo.commons.api.domain.v4.ODataLinkedComplexValue;
 import org.apache.olingo.commons.api.domain.v4.ODataProperty;
 import org.apache.olingo.commons.api.domain.v4.ODataValue;
 import org.apache.olingo.commons.core.domain.AbstractODataProperty;
@@ -41,7 +42,9 @@ public class ODataPropertyImpl extends AbstractODataProperty implements ODataPro
 
   @Override
   public ODataEnumValue getEnumValue() {
-    return hasEnumValue() ? ((org.apache.olingo.commons.api.domain.v4.ODataValue) getValue()).asEnum() : null;
+    return hasEnumValue()
+            ? ((org.apache.olingo.commons.api.domain.v4.ODataValue) getValue()).asEnum()
+            : null;
   }
 
   @Override
@@ -50,8 +53,17 @@ public class ODataPropertyImpl extends AbstractODataProperty implements ODataPro
   }
 
   @Override
+  public ODataLinkedComplexValue getLinkedComplexValue() {
+    return hasComplexValue() && getValue() instanceof org.apache.olingo.commons.api.domain.v4.ODataValue
+            ? ((org.apache.olingo.commons.api.domain.v4.ODataValue) getValue()).asLinkedComplex()
+            : null;
+  }
+
+  @Override
   public ODataCollectionValue<ODataValue> getCollectionValue() {
-    return hasCollectionValue() ? getValue().<ODataValue>asCollection() : null;
+    return hasCollectionValue()
+            ? getValue().<ODataValue>asCollection()
+            : null;
   }
 
 }