You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by il...@apache.org on 2014/03/24 10:42:24 UTC

[35/50] [abbrv] [OLINGO-200] Moving Atom and JSON (de)serializer to commons

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/fac84b3e/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
new file mode 100644
index 0000000..99231b4
--- /dev/null
+++ b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AtomPropertyDeserializer.java
@@ -0,0 +1,214 @@
+/*
+ * 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.ComplexValue;
+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;
+  }
+
+  private ComplexValue fromComplex(final XMLEventReader reader, final StartElement start)
+          throws XMLStreamException {
+
+    final ComplexValue value = new ComplexValueImpl();
+
+    boolean foundEndProperty = false;
+    while (reader.hasNext() && !foundEndProperty) {
+      final XMLEvent event = reader.nextEvent();
+
+      if (event.isStartElement()) {
+        value.get().add(deserialize(reader, event.asStartElement()));
+      }
+
+      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)) {
+          case COMPLEX:
+            value.get().add(fromComplex(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) 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;
+      }
+    }
+
+    ODataPropertyType type = null;
+    if (child == null) {
+      type = ODataPropertyType.PRIMITIVE;
+    } 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 = ODataPropertyType.PRIMITIVE;
+      } else {
+        type = ODataPropertyType.EMPTY;
+      }
+    }
+
+    return type;
+  }
+
+  public AtomPropertyImpl deserialize(final XMLEventReader reader, final StartElement start)
+          throws XMLStreamException {
+
+    final AtomPropertyImpl property = new AtomPropertyImpl();
+    property.setName(start.getName().getLocalPart());
+
+    final Attribute typeAttr = start.getAttributeByName(this.typeQName);
+    if (typeAttr != null) {
+      property.setType(typeAttr.getValue());
+    }
+
+    Value value;
+    final Attribute nullAttr = start.getAttributeByName(this.nullQName);
+    if (nullAttr == null) {
+      final EdmTypeInfo typeInfo = StringUtils.isBlank(property.getType())
+              ? null
+              : new EdmTypeInfo.Builder().setTypeExpression(property.getType()).build();
+
+      final ODataPropertyType propType = typeInfo == null
+              ? guessPropertyType(reader)
+              : typeInfo.isCollection()
+              ? ODataPropertyType.COLLECTION
+              : typeInfo.isPrimitiveType()
+              ? ODataPropertyType.PRIMITIVE
+              : ODataPropertyType.COMPLEX;
+
+      switch (propType) {
+        case COLLECTION:
+          value = fromCollection(reader, start, typeInfo);
+          break;
+
+        case COMPLEX:
+          value = fromComplex(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/incubator-olingo-odata4/blob/fac84b3e/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AtomPropertyImpl.java
----------------------------------------------------------------------
diff --git a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AtomPropertyImpl.java b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AtomPropertyImpl.java
new file mode 100644
index 0000000..9688db2
--- /dev/null
+++ b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AtomPropertyImpl.java
@@ -0,0 +1,25 @@
+/*
+ * 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;
+
+public class AtomPropertyImpl extends AbstractPropertyImpl {
+
+  private static final long serialVersionUID = 48748492242474814L;
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/fac84b3e/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
new file mode 100644
index 0000000..8d2a8e1
--- /dev/null
+++ b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AtomPropertySerializer.java
@@ -0,0 +1,88 @@
+/*
+ * 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;
+
+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()) {
+      writer.writeStartElement(Constants.PREFIX_DATASERVICES, Constants.ELEM_ELEMENT,
+              version.getNamespaceMap().get(ODataServiceVersion.NS_DATASERVICES));
+      value(writer, item);
+      writer.writeEndElement();
+    }
+  }
+
+  private void value(final XMLStreamWriter writer, final Value value) throws XMLStreamException {
+    if (value.isSimple()) {
+      writer.writeCharacters(value.asSimple().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 {
+
+    writer.writeStartElement(Constants.PREFIX_DATASERVICES, property.getName(),
+            version.getNamespaceMap().get(ODataServiceVersion.NS_DATASERVICES));
+    if (standalone) {
+      namespaces(writer);
+    }
+    if (StringUtils.isNotBlank(property.getType())) {
+      writer.writeAttribute(Constants.PREFIX_METADATA, version.getNamespaceMap().get(ODataServiceVersion.NS_METADATA),
+              Constants.ATTR_TYPE, property.getType());
+    }
+
+    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/incubator-olingo-odata4/blob/fac84b3e/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
new file mode 100644
index 0000000..4c6fb3a
--- /dev/null
+++ b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/AtomSerializer.java
@@ -0,0 +1,264 @@
+/*
+ * 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.io.Writer;
+import java.util.Collections;
+import java.util.List;
+import javax.xml.XMLConstants;
+import javax.xml.stream.XMLOutputFactory;
+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.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.edm.constants.ODataServiceVersion;
+import org.apache.olingo.commons.api.format.ContentType;
+
+public class AtomSerializer extends AbstractAtomDealer {
+
+  private static final XMLOutputFactory FACTORY = XMLOutputFactory.newInstance();
+
+  private final AtomPropertySerializer propSerializer;
+
+  public AtomSerializer(final ODataServiceVersion version) {
+    super(version);
+    this.propSerializer = new AtomPropertySerializer(version);
+  }
+
+  private void startDocument(final XMLStreamWriter writer, final String rootElement) throws XMLStreamException {
+    writer.writeStartDocument();
+    writer.setDefaultNamespace(Constants.NS_ATOM);
+
+    writer.writeStartElement(rootElement);
+
+    namespaces(writer);
+  }
+
+  private void property(final Writer outWriter, final Property property) throws XMLStreamException {
+    final XMLStreamWriter writer = FACTORY.createXMLStreamWriter(outWriter);
+
+    writer.writeStartDocument();
+
+    propSerializer.property(writer, property);
+
+    writer.writeEndDocument();
+    writer.flush();
+  }
+
+  private void links(final XMLStreamWriter writer, final List<Link> links) throws XMLStreamException {
+    for (Link link : links) {
+      writer.writeStartElement(Constants.ATOM_ELEM_LINK);
+
+      if (StringUtils.isNotBlank(link.getRel())) {
+        writer.writeAttribute(Constants.ATTR_REL, link.getRel());
+      }
+      if (StringUtils.isNotBlank(link.getTitle())) {
+        writer.writeAttribute(Constants.ATTR_TITLE, link.getTitle());
+      }
+      if (StringUtils.isNotBlank(link.getHref())) {
+        writer.writeAttribute(Constants.ATTR_HREF, link.getHref());
+      }
+      if (StringUtils.isNotBlank(link.getType())) {
+        writer.writeAttribute(Constants.ATTR_TYPE, link.getType());
+      }
+
+      if (link.getInlineEntry() != null || link.getInlineFeed() != null) {
+        writer.writeStartElement(Constants.PREFIX_METADATA, Constants.ATOM_ELEM_INLINE,
+                version.getNamespaceMap().get(ODataServiceVersion.NS_METADATA));
+
+        if (link.getInlineEntry() != null) {
+          writer.writeStartElement(Constants.ATOM_ELEM_ENTRY);
+          entry(writer, link.getInlineEntry());
+          writer.writeEndElement();
+        }
+        if (link.getInlineFeed() != null) {
+          writer.writeStartElement(Constants.ATOM_ELEM_FEED);
+          feed(writer, link.getInlineFeed());
+          writer.writeEndElement();
+        }
+
+        writer.writeEndElement();
+      }
+
+      writer.writeEndElement();
+    }
+  }
+
+  private void common(final XMLStreamWriter writer, final AbstractAtomObject object) throws XMLStreamException {
+    if (StringUtils.isNotBlank(object.getTitle())) {
+      writer.writeStartElement(Constants.ATOM_ELEM_TITLE);
+      writer.writeAttribute(Constants.ATTR_TYPE, TYPE_TEXT);
+      writer.writeCharacters(object.getTitle());
+      writer.writeEndElement();
+    }
+
+    if (StringUtils.isNotBlank(object.getSummary())) {
+      writer.writeStartElement(Constants.ATOM_ELEM_SUMMARY);
+      writer.writeAttribute(Constants.ATTR_TYPE, "text");
+      writer.writeCharacters(object.getSummary());
+      writer.writeEndElement();
+    }
+  }
+
+  private void properties(final XMLStreamWriter writer, final List<Property> properties) throws XMLStreamException {
+    for (Property property : properties) {
+      propSerializer.property(writer, property, false);
+    }
+  }
+
+  private void entry(final XMLStreamWriter writer, final Entry entry) throws XMLStreamException {
+    if (entry.getBaseURI() != null) {
+      writer.writeAttribute(XMLConstants.XML_NS_URI, Constants.ATTR_XML_BASE, entry.getBaseURI().toASCIIString());
+    }
+
+    if (StringUtils.isNotBlank(entry.getId())) {
+      writer.writeStartElement(Constants.ATOM_ELEM_ID);
+      writer.writeCharacters(entry.getId());
+      writer.writeEndElement();
+    }
+
+    writer.writeStartElement(Constants.ATOM_ELEM_CATEGORY);
+    writer.writeAttribute(Constants.ATOM_ATTR_SCHEME, version.getNamespaceMap().get(ODataServiceVersion.NS_SCHEME));
+    writer.writeAttribute(Constants.ATOM_ATTR_TERM, entry.getType());
+    writer.writeEndElement();
+
+    if (entry instanceof AbstractAtomObject) {
+      common(writer, (AbstractAtomObject) entry);
+    }
+
+    links(writer, entry.getAssociationLinks());
+    links(writer, entry.getNavigationLinks());
+    links(writer, entry.getMediaEditLinks());
+
+    writer.writeStartElement(Constants.ATOM_ELEM_CONTENT);
+    if (entry.isMediaEntry()) {
+      if (StringUtils.isNotBlank(entry.getMediaContentType())) {
+        writer.writeAttribute(Constants.ATTR_TYPE, entry.getMediaContentType());
+      }
+      if (StringUtils.isNotBlank(entry.getMediaContentSource())) {
+        writer.writeAttribute(Constants.ATOM_ATTR_SRC, entry.getMediaContentSource());
+      }
+      writer.writeEndElement();
+
+      writer.writeStartElement(version.getNamespaceMap().get(ODataServiceVersion.NS_METADATA), Constants.PROPERTIES);
+      properties(writer, entry.getProperties());
+    } else {
+      writer.writeAttribute(Constants.ATTR_TYPE, ContentType.APPLICATION_XML);
+      writer.writeStartElement(version.getNamespaceMap().get(ODataServiceVersion.NS_METADATA), Constants.PROPERTIES);
+      properties(writer, entry.getProperties());
+      writer.writeEndElement();
+    }
+    writer.writeEndElement();
+  }
+
+  private void entry(final Writer outWriter, final Entry entry) throws XMLStreamException {
+    final XMLStreamWriter writer = FACTORY.createXMLStreamWriter(outWriter);
+
+    startDocument(writer, Constants.ATOM_ELEM_ENTRY);
+
+    entry(writer, entry);
+
+    writer.writeEndElement();
+    writer.writeEndDocument();
+    writer.flush();
+  }
+
+  private void feed(final XMLStreamWriter writer, final Feed feed) throws XMLStreamException {
+    if (feed.getBaseURI() != null) {
+      writer.writeAttribute(XMLConstants.XML_NS_URI, Constants.ATTR_XML_BASE, feed.getBaseURI().toASCIIString());
+    }
+
+    if (feed.getCount() != null) {
+      writer.writeStartElement(
+              version.getNamespaceMap().get(ODataServiceVersion.NS_METADATA), Constants.ATOM_ELEM_COUNT);
+      writer.writeCharacters(Integer.toString(feed.getCount()));
+      writer.writeEndElement();
+    }
+
+    if (StringUtils.isNotBlank(feed.getId())) {
+      writer.writeStartElement(Constants.ATOM_ELEM_ID);
+      writer.writeCharacters(feed.getId());
+      writer.writeEndElement();
+    }
+
+    if (feed instanceof AbstractAtomObject) {
+      common(writer, (AbstractAtomObject) feed);
+    }
+
+    for (Entry entry : feed.getEntries()) {
+      writer.writeStartElement(Constants.ATOM_ELEM_ENTRY);
+      entry(writer, entry);
+      writer.writeEndElement();
+    }
+
+    if (feed.getNext() != null) {
+      final LinkImpl next = new LinkImpl();
+      next.setRel(Constants.NEXT_LINK_REL);
+      next.setHref(feed.getNext().toASCIIString());
+
+      links(writer, Collections.<Link>singletonList(next));
+    }
+  }
+
+  private void feed(final Writer outWriter, final Feed feed) throws XMLStreamException {
+    final XMLStreamWriter writer = FACTORY.createXMLStreamWriter(outWriter);
+
+    startDocument(writer, Constants.ATOM_ELEM_FEED);
+
+    feed(writer, feed);
+
+    writer.writeEndElement();
+    writer.writeEndDocument();
+    writer.flush();
+  }
+
+  private void link(final Writer outWriter, final Link link) throws XMLStreamException {
+    final XMLStreamWriter writer = FACTORY.createXMLStreamWriter(outWriter);
+
+    writer.writeStartDocument();
+
+    writer.writeStartElement(Constants.ELEM_LINKS);
+    writer.writeDefaultNamespace(version.getNamespaceMap().get(ODataServiceVersion.NS_DATASERVICES));
+
+    writer.writeStartElement(Constants.ELEM_URI);
+    writer.writeCharacters(link.getHref());
+    writer.writeEndElement();
+
+    writer.writeEndElement();
+
+    writer.writeEndDocument();
+    writer.flush();
+  }
+
+  public <T> void write(final Writer writer, final T obj) throws XMLStreamException {
+    if (obj instanceof Feed) {
+      feed(writer, (Feed) obj);
+    } else if (obj instanceof Entry) {
+      entry(writer, (Entry) obj);
+    } else if (obj instanceof Property) {
+      property(writer, (Property) obj);
+    } else if (obj instanceof Link) {
+      link(writer, (Link) obj);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/fac84b3e/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/CollectionValueImpl.java
----------------------------------------------------------------------
diff --git a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/CollectionValueImpl.java b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/CollectionValueImpl.java
new file mode 100644
index 0000000..02c6d43
--- /dev/null
+++ b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/CollectionValueImpl.java
@@ -0,0 +1,40 @@
+/*
+ * 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.CollectionValue;
+import org.apache.olingo.commons.api.data.Value;
+
+public class CollectionValueImpl extends AbstractValue implements CollectionValue {
+
+  private final List<Value> value = new ArrayList<Value>();
+
+  @Override
+  public boolean isCollection() {
+    return true;
+  }
+
+  @Override
+  public List<Value> get() {
+    return value;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/fac84b3e/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/ComplexValueImpl.java
----------------------------------------------------------------------
diff --git a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/ComplexValueImpl.java b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/ComplexValueImpl.java
new file mode 100644
index 0000000..0144f83
--- /dev/null
+++ b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/ComplexValueImpl.java
@@ -0,0 +1,40 @@
+/*
+ * 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.ComplexValue;
+import org.apache.olingo.commons.api.data.Property;
+
+public class ComplexValueImpl extends AbstractValue implements ComplexValue {
+
+  private final List<Property> value = new ArrayList<Property>();
+
+  @Override
+  public boolean isComplex() {
+    return true;
+  }
+
+  @Override
+  public List<Property> get() {
+    return value;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/fac84b3e/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/GeospatialValueImpl.java
----------------------------------------------------------------------
diff --git a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/GeospatialValueImpl.java b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/GeospatialValueImpl.java
new file mode 100644
index 0000000..e278334
--- /dev/null
+++ b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/GeospatialValueImpl.java
@@ -0,0 +1,42 @@
+/*
+ * 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 org.apache.olingo.commons.api.data.GeospatialValue;
+import org.apache.olingo.commons.api.edm.geo.Geospatial;
+
+public class GeospatialValueImpl extends AbstractValue implements GeospatialValue {
+
+  private final Geospatial value;
+
+  public GeospatialValueImpl(final Geospatial value) {
+    this.value = value;
+  }
+
+  @Override
+  public boolean isGeospatial() {
+    return true;
+  }
+
+  @Override
+  public Geospatial get() {
+    return value;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/fac84b3e/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
new file mode 100644
index 0000000..3647fac
--- /dev/null
+++ b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONEntryDeserializer.java
@@ -0,0 +1,241 @@
+/*
+ * 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 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.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonMappingException;
+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;
+import java.text.ParseException;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import org.apache.olingo.commons.api.Constants;
+import org.apache.olingo.commons.api.data.Link;
+import org.apache.olingo.commons.api.domain.ODataLinkType;
+import org.apache.olingo.commons.api.domain.ODataOperation;
+import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
+
+/**
+ * Reads JSON string into an entry.
+ * <br/>
+ * If metadata information is available, the corresponding entry fields and content will be populated.
+ */
+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).readValuesAs(JSONEntryImpl.class).next());
+      }
+
+      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).readValuesAs(JSONEntryImpl.class).next());
+        }
+
+        link.setInlineFeed(feed);
+      }
+    }
+    return entryNamePrefix;
+  }
+
+  @Override
+  protected JSONEntryImpl doDeserialize(final JsonParser parser, final DeserializationContext ctxt)
+          throws IOException, JsonProcessingException {
+
+    final ObjectNode tree = (ObjectNode) parser.getCodec().readTree(parser);
+
+    if (tree.has(Constants.JSON_VALUE) && tree.get(Constants.JSON_VALUE).isArray()) {
+      throw new JsonParseException("Expected OData Entity, found EntitySet", parser.getCurrentLocation());
+    }
+
+    final JSONEntryImpl entry = new JSONEntryImpl();
+
+    if (tree.hasNonNull(Constants.JSON_METADATA)) {
+      entry.setMetadata(URI.create(tree.get(Constants.JSON_METADATA).textValue()));
+      tree.remove(Constants.JSON_METADATA);
+    }
+
+    if (tree.hasNonNull(Constants.JSON_MEDIA_ETAG)) {
+      entry.setMediaETag(tree.get(Constants.JSON_MEDIA_ETAG).textValue());
+      tree.remove(Constants.JSON_MEDIA_ETAG);
+    }
+
+    if (tree.hasNonNull(Constants.JSON_ETAG)) {
+      entry.setETag(tree.get(Constants.JSON_ETAG).textValue());
+      tree.remove(Constants.JSON_ETAG);
+    }
+
+    if (tree.hasNonNull(Constants.JSON_TYPE)) {
+      entry.setType(tree.get(Constants.JSON_TYPE).textValue());
+      tree.remove(Constants.JSON_TYPE);
+    }
+
+    if (tree.hasNonNull(Constants.JSON_ID)) {
+      try {
+        entry.setId(tree.get(Constants.JSON_ID).textValue());
+      } catch (ParseException e) {
+        throw new JsonMappingException("While parsing Atom entry or feed common elements", e);
+      }
+      tree.remove(Constants.JSON_ID);
+    }
+
+    if (tree.hasNonNull(Constants.JSON_READ_LINK)) {
+      final LinkImpl link = new LinkImpl();
+      link.setRel(Constants.SELF_LINK_REL);
+      link.setHref(tree.get(Constants.JSON_READ_LINK).textValue());
+      entry.setSelfLink(link);
+
+      tree.remove(Constants.JSON_READ_LINK);
+    }
+
+    if (tree.hasNonNull(Constants.JSON_EDIT_LINK)) {
+      final LinkImpl link = new LinkImpl();
+      link.setRel(Constants.EDIT_LINK_REL);
+      link.setHref(tree.get(Constants.JSON_EDIT_LINK).textValue());
+      entry.setEditLink(link);
+
+      tree.remove(Constants.JSON_EDIT_LINK);
+    }
+
+    if (tree.hasNonNull(Constants.JSON_MEDIAREAD_LINK)) {
+      entry.setMediaContentSource(tree.get(Constants.JSON_MEDIAREAD_LINK).textValue());
+      tree.remove(Constants.JSON_MEDIAREAD_LINK);
+    }
+    if (tree.hasNonNull(Constants.JSON_MEDIAEDIT_LINK)) {
+      tree.remove(Constants.JSON_MEDIAEDIT_LINK);
+    }
+    if (tree.hasNonNull(Constants.JSON_MEDIA_CONTENT_TYPE)) {
+      entry.setMediaContentType(tree.get(Constants.JSON_MEDIA_CONTENT_TYPE).textValue());
+      tree.remove(Constants.JSON_MEDIA_CONTENT_TYPE);
+    }
+
+    final Set<String> toRemove = new HashSet<String>();
+    for (final Iterator<Map.Entry<String, JsonNode>> itor = tree.fields(); itor.hasNext();) {
+      final Map.Entry<String, JsonNode> field = itor.next();
+
+      if (field.getKey().endsWith(Constants.JSON_NAVIGATION_LINK_SUFFIX)) {
+        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(), Constants.JSON_NAVIGATION_LINK_SUFFIX, tree, parser.getCodec(), link));
+      } else if (field.getKey().endsWith(Constants.JSON_ASSOCIATION_LINK_SUFFIX)) {
+        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(Constants.JSON_MEDIAEDIT_LINK_SUFFIX)) {
+        final LinkImpl link = new LinkImpl();
+        link.setTitle(getTitle(field));
+        link.setRel(version.getNamespaceMap().get(ODataServiceVersion.MEDIA_EDIT_LINK_REL) + getTitle(field));
+        link.setHref(field.getValue().textValue());
+        link.setType(ODataLinkType.MEDIA_EDIT.toString());
+        entry.getMediaEditLinks().add(link);
+
+        if (tree.has(link.getTitle() + Constants.JSON_MEDIA_ETAG_SUFFIX)) {
+          link.setMediaETag(tree.get(link.getTitle() + Constants.JSON_MEDIA_ETAG_SUFFIX).asText());
+          toRemove.add(link.getTitle() + Constants.JSON_MEDIA_ETAG_SUFFIX);
+        }
+
+        toRemove.add(field.getKey());
+        toRemove.add(setInline(field.getKey(), Constants.JSON_MEDIAEDIT_LINK_SUFFIX, tree, parser.getCodec(), link));
+      } else if (field.getKey().endsWith(Constants.JSON_MEDIA_CONTENT_TYPE)) {
+        final String linkTitle = getTitle(field);
+        for (Link link : entry.getMediaEditLinks()) {
+          if (linkTitle.equals(link.getTitle())) {
+            ((LinkImpl) link).setType(field.getValue().asText());
+          }
+        }
+        toRemove.add(field.getKey());
+      } else if (field.getKey().charAt(0) == '#') {
+        final ODataOperation operation = new ODataOperation();
+        operation.setMetadataAnchor(field.getKey());
+
+        final ObjectNode opNode = (ObjectNode) tree.get(field.getKey());
+        operation.setTitle(opNode.get(Constants.ATTR_TITLE).asText());
+        operation.setTarget(URI.create(opNode.get(Constants.ATTR_TARGET).asText()));
+
+        entry.getOperations().add(operation);
+
+        toRemove.add(field.getKey());
+      }
+    }
+    tree.remove(toRemove);
+
+    String type = null;
+    for (final Iterator<Map.Entry<String, JsonNode>> itor = tree.fields(); itor.hasNext();) {
+      final Map.Entry<String, JsonNode> field = itor.next();
+
+      if (type == null && field.getKey().endsWith(Constants.JSON_TYPE_SUFFIX)) {
+        type = field.getValue().asText();
+      } else {
+        final JSONPropertyImpl property = new JSONPropertyImpl();
+        property.setName(field.getKey());
+        property.setType(type);
+        type = null;
+
+        value(property, field.getValue());
+        entry.getProperties().add(property);
+      }
+    }
+
+    return entry;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/fac84b3e/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONEntryImpl.java
----------------------------------------------------------------------
diff --git a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONEntryImpl.java b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONEntryImpl.java
new file mode 100644
index 0000000..765c18a
--- /dev/null
+++ b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONEntryImpl.java
@@ -0,0 +1,92 @@
+/*
+ * 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 com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import java.net.URI;
+import java.text.ParseException;
+import org.apache.olingo.commons.api.Constants;
+
+/**
+ * A single entry, represented via JSON.
+ */
+@JsonSerialize(using = JSONEntrySerializer.class)
+@JsonDeserialize(using = JSONEntryDeserializer.class)
+public class JSONEntryImpl extends AbstractEntry {
+
+  private static final long serialVersionUID = -5275365545400797758L;
+
+  private URI metadata;
+
+  private String mediaETag;
+
+  public void setId(final String id) throws ParseException {
+    this.setCommonProperty("id", id);
+  }
+
+  @Override
+  public URI getBaseURI() {
+    URI baseURI = null;
+    if (metadata != null) {
+      final String metadataURI = getMetadata().toASCIIString();
+      baseURI = URI.create(metadataURI.substring(0, metadataURI.indexOf(Constants.METADATA)));
+    }
+
+    return baseURI;
+  }
+
+  /**
+   * Gets the metadata URI.
+   *
+   * @return the metadata URI
+   */
+  public URI getMetadata() {
+    return metadata;
+  }
+
+  /**
+   * Sets the metadata URI.
+   *
+   * @param metadata metadata URI.
+   */
+  public void setMetadata(final URI metadata) {
+    this.metadata = metadata;
+  }
+
+  /**
+   * The odata.mediaEtag annotation MAY be included; its value MUST be the ETag of the binary stream represented by this
+   * media entity or named stream property.
+   *
+   * @return odata.mediaEtag annotation value.
+   */
+  public String getMediaETag() {
+    return mediaETag;
+  }
+
+  /**
+   * The odata.mediaEtag annotation MAY be included; its value MUST be the ETag of the binary stream represented by this
+   * media entity or named stream property.
+   *
+   * @param eTag odata.mediaEtag annotation value.
+   */
+  public void setMediaETag(final String eTag) {
+    this.mediaETag = eTag;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/fac84b3e/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
new file mode 100644
index 0000000..4041748
--- /dev/null
+++ b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONEntrySerializer.java
@@ -0,0 +1,120 @@
+/*
+ * 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 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;
+
+/**
+ * Writes out JSON string from an entry.
+ */
+public class JSONEntrySerializer extends AbstractJsonSerializer<JSONEntryImpl> {
+
+  @Override
+  protected void doSerialize(final JSONEntryImpl entry, final JsonGenerator jgen, final SerializerProvider provider)
+          throws IOException, JsonProcessingException {
+
+    jgen.writeStartObject();
+
+    if (entry.getMetadata() != null) {
+      jgen.writeStringField(Constants.JSON_METADATA, entry.getMetadata().toASCIIString());
+    }
+    if (entry.getId() != null) {
+      jgen.writeStringField(Constants.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 (Link link : entry.getMediaEditLinks()) {
+      if (link.getTitle() == null) {
+        jgen.writeStringField(Constants.JSON_MEDIAEDIT_LINK, link.getHref());
+      }
+
+      if (link.getInlineEntry() != null) {
+        jgen.writeObjectField(link.getTitle(), link.getInlineEntry());
+      }
+      if (link.getInlineFeed() != null) {
+        jgen.writeArrayFieldStart(link.getTitle());
+        for (Entry subEntry : link.getInlineFeed().getEntries()) {
+          jgen.writeObject(subEntry);
+        }
+        jgen.writeEndArray();
+      }
+    }
+
+    for (Property property : entry.getProperties()) {
+      property(jgen, property, property.getName());
+    }
+
+    jgen.writeEndObject();
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/fac84b3e/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONErrorBundle.java
----------------------------------------------------------------------
diff --git a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONErrorBundle.java b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONErrorBundle.java
new file mode 100644
index 0000000..433b754
--- /dev/null
+++ b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONErrorBundle.java
@@ -0,0 +1,50 @@
+/*
+ * 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 com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * This class represents a bundle for an OData error returned as JSON.
+ */
+public class JSONErrorBundle extends AbstractPayloadObject {
+
+  private static final long serialVersionUID = -4784910226259754450L;
+
+  @JsonProperty("odata.error")
+  private JSONErrorImpl error;
+
+  /**
+   * Gets error.
+   *
+   * @return OData error object.
+   */
+  public JSONErrorImpl getError() {
+    return error;
+  }
+
+  /**
+   * Sets error.
+   *
+   * @param error OData error object.
+   */
+  public void setError(final JSONErrorImpl error) {
+    this.error = error;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/fac84b3e/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONErrorImpl.java
----------------------------------------------------------------------
diff --git a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONErrorImpl.java b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONErrorImpl.java
new file mode 100644
index 0000000..4c6cb4a
--- /dev/null
+++ b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONErrorImpl.java
@@ -0,0 +1,237 @@
+/*
+ * 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 com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import org.apache.olingo.commons.api.domain.ODataError;
+
+/**
+ * This class represents an OData error returned as JSON.
+ */
+public class JSONErrorImpl extends AbstractPayloadObject implements ODataError {
+
+  private static final long serialVersionUID = -3476499168507242932L;
+
+  /**
+   * Error message.
+   */
+  public static class Message extends AbstractPayloadObject {
+
+    private static final long serialVersionUID = 2577818040815637859L;
+
+    private String lang;
+
+    private String value;
+
+    /**
+     * Gets language.
+     *
+     * @return language.
+     */
+    public String getLang() {
+      return lang;
+    }
+
+    /**
+     * Sets language.
+     *
+     * @param lang language.
+     */
+    public void setLang(final String lang) {
+      this.lang = lang;
+    }
+
+    /**
+     * Gets message.
+     *
+     * @return message.
+     */
+    public String getValue() {
+      return value;
+    }
+
+    /**
+     * Sets message.
+     *
+     * @param value message.
+     */
+    public void setValue(final String value) {
+      this.value = value;
+    }
+  }
+
+  /**
+   * Inner error.
+   */
+  static class InnerError extends AbstractPayloadObject {
+
+    private static final long serialVersionUID = -3920947476143537640L;
+
+    private String message;
+
+    private String type;
+
+    private String stacktrace;
+
+    private InnerError internalexception;
+
+    /**
+     * Gets inner message.
+     *
+     * @return message.
+     */
+    public String getMessage() {
+      return message;
+    }
+
+    /**
+     * Sets inner message.
+     *
+     * @param message message.
+     */
+    public void setMessage(final String message) {
+      this.message = message;
+    }
+
+    /**
+     * Gets type.
+     *
+     * @return type.
+     */
+    public String getType() {
+      return type;
+    }
+
+    /**
+     * Sets type.
+     *
+     * @param type type.
+     */
+    public void setType(final String type) {
+      this.type = type;
+    }
+
+    /**
+     * Gets stack-trace.
+     *
+     * @return stack-trace.
+     */
+    public String getStacktrace() {
+      return stacktrace;
+    }
+
+    /**
+     * Sets stack-trace.
+     *
+     * @param stacktrace stack-trace.
+     */
+    public void setStacktrace(final String stacktrace) {
+      this.stacktrace = stacktrace;
+    }
+
+    public InnerError getInternalexception() {
+      return internalexception;
+    }
+
+    public void setInternalexception(final InnerError internalexception) {
+      this.internalexception = internalexception;
+    }
+  }
+
+  private String code;
+
+  @JsonProperty(value = "message")
+  private Message message;
+
+  @JsonProperty(value = "innererror", required = false)
+  private InnerError innererror;
+
+  /**
+   * {@inheritDoc }
+   */
+  @Override
+  public String getCode() {
+    return code;
+  }
+
+  /**
+   * Sets error code.
+   *
+   * @param code error code.
+   */
+  public void setCode(final String code) {
+    this.code = code;
+  }
+
+  /**
+   * {@inheritDoc }
+   */
+  @JsonIgnore
+  @Override
+  public String getMessageLang() {
+    return this.message == null ? null : this.message.getLang();
+  }
+
+  /**
+   * {@inheritDoc }
+   */
+  @JsonIgnore
+  @Override
+  public String getMessageValue() {
+    return this.message == null ? null : this.message.getValue();
+  }
+
+  /**
+   * Sets the value of the message property.
+   *
+   * @param value allowed object is {@link Error.Message }
+   *
+   */
+  public void setMessage(final Message value) {
+    this.message = value;
+  }
+
+  /**
+   * {@inheritDoc }
+   */
+  @JsonIgnore
+  @Override
+  public String getInnerErrorMessage() {
+    return this.innererror == null ? null : this.innererror.getMessage();
+  }
+
+  /**
+   * {@inheritDoc }
+   */
+  @JsonIgnore
+  @Override
+  public String getInnerErrorType() {
+    return this.innererror == null ? null : this.innererror.getType();
+  }
+
+  /**
+   * {@inheritDoc }
+   */
+  @JsonIgnore
+  @Override
+  public String getInnerErrorStacktrace() {
+    return this.innererror == null ? null : this.innererror.getStacktrace();
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/fac84b3e/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONFeedDeserializer.java
----------------------------------------------------------------------
diff --git a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONFeedDeserializer.java b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONFeedDeserializer.java
new file mode 100644
index 0000000..81a8d47
--- /dev/null
+++ b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONFeedDeserializer.java
@@ -0,0 +1,68 @@
+/*
+ * 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 com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import java.io.IOException;
+import java.net.URI;
+import java.util.Iterator;
+import org.apache.olingo.commons.api.Constants;
+
+/**
+ * Reads JSON string into a feed.
+ * <br/>
+ * If metadata information is available, the corresponding entry fields and content will be populated.
+ */
+public class JSONFeedDeserializer extends AbstractJsonDeserializer<JSONFeedImpl> {
+
+  @Override
+  protected JSONFeedImpl doDeserialize(final JsonParser parser, final DeserializationContext ctxt)
+          throws IOException, JsonProcessingException {
+
+    final ObjectNode tree = (ObjectNode) parser.getCodec().readTree(parser);
+
+    if (!tree.has(Constants.JSON_VALUE)) {
+      return null;
+    }
+
+    final JSONFeedImpl feed = new JSONFeedImpl();
+
+    if (tree.hasNonNull(Constants.JSON_METADATA)) {
+      feed.setMetadata(URI.create(tree.get(Constants.JSON_METADATA).textValue()));
+    }
+    if (tree.hasNonNull(Constants.JSON_COUNT)) {
+      feed.setCount(tree.get(Constants.JSON_COUNT).asInt());
+    }
+    if (tree.hasNonNull(Constants.JSON_NEXT_LINK)) {
+      feed.setNext(URI.create(tree.get(Constants.JSON_NEXT_LINK).textValue()));
+    }
+
+    if (tree.hasNonNull(Constants.JSON_VALUE)) {
+      for (final Iterator<JsonNode> itor = tree.get(Constants.JSON_VALUE).iterator(); itor.hasNext();) {
+        feed.getEntries().add(itor.next().traverse(parser.getCodec()).readValueAs(JSONEntryImpl.class));
+      }
+    }
+
+    return feed;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/fac84b3e/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONFeedImpl.java
----------------------------------------------------------------------
diff --git a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONFeedImpl.java b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONFeedImpl.java
new file mode 100644
index 0000000..f41e21c
--- /dev/null
+++ b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONFeedImpl.java
@@ -0,0 +1,113 @@
+/*
+ * 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 com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.olingo.commons.api.data.Entry;
+import org.apache.olingo.commons.api.data.Feed;
+import org.apache.olingo.commons.api.Constants;
+
+/**
+ * List of entries, represented via JSON.
+ *
+ * @see JSONEntry
+ */
+@JsonDeserialize(using = JSONFeedDeserializer.class)
+@JsonSerialize(using = JSONFeedSerializer.class)
+public class JSONFeedImpl extends AbstractPayloadObject implements Feed {
+
+  private static final long serialVersionUID = -3576372289800799417L;
+
+  private String id;
+
+  private URI metadata;
+
+  private Integer count;
+
+  private final List<Entry> entries = new ArrayList<Entry>();
+
+  private String next;
+
+  @Override
+  public URI getBaseURI() {
+    URI baseURI = null;
+    if (metadata != null) {
+      final String metadataURI = getMetadata().toASCIIString();
+      baseURI = URI.create(metadataURI.substring(0, metadataURI.indexOf(Constants.METADATA)));
+    }
+
+    return baseURI;
+  }
+
+  /**
+   * Gets the metadata URI.
+   *
+   * @return the metadata URI
+   */
+  public URI getMetadata() {
+    return metadata;
+  }
+
+  /**
+   * Sets the metadata URI.
+   *
+   * @param metadata metadata URI.
+   */
+  public void setMetadata(final URI metadata) {
+    this.metadata = metadata;
+  }
+
+  @Override
+  public String getId() {
+    return id;
+  }
+
+  public void setId(final String id) {
+    this.id = id;
+  }
+
+  @Override
+  public Integer getCount() {
+    return count;
+  }
+
+  @Override
+  public void setCount(final Integer count) {
+    this.count = count;
+  }
+
+  @Override
+  public List<Entry> getEntries() {
+    return entries;
+  }
+
+  @Override
+  public void setNext(final URI next) {
+    this.next = next.toASCIIString();
+  }
+
+  @Override
+  public URI getNext() {
+    return next == null ? null : URI.create(next);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/fac84b3e/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONFeedSerializer.java
----------------------------------------------------------------------
diff --git a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONFeedSerializer.java b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONFeedSerializer.java
new file mode 100644
index 0000000..25a7358
--- /dev/null
+++ b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONFeedSerializer.java
@@ -0,0 +1,57 @@
+/*
+ * 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 com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import java.io.IOException;
+import org.apache.olingo.commons.api.Constants;
+import org.apache.olingo.commons.api.data.Entry;
+
+public class JSONFeedSerializer extends AbstractJsonSerializer<JSONFeedImpl> {
+
+  @Override
+  protected void doSerialize(final JSONFeedImpl feed, final JsonGenerator jgen, final SerializerProvider provider)
+          throws IOException, JsonProcessingException {
+
+    jgen.writeStartObject();
+
+    if (feed.getMetadata() != null) {
+      jgen.writeStringField(Constants.JSON_METADATA, feed.getMetadata().toASCIIString());
+    }
+    if (feed.getId() != null) {
+      jgen.writeStringField(Constants.JSON_ID, feed.getId());
+    }
+    if (feed.getCount() != null) {
+      jgen.writeNumberField(Constants.JSON_COUNT, feed.getCount());
+    }
+    if (feed.getNext() != null) {
+      jgen.writeStringField(Constants.JSON_NEXT_LINK, feed.getNext().toASCIIString());
+    }
+
+    jgen.writeArrayFieldStart(Constants.JSON_VALUE);
+    for (Entry entry : feed.getEntries()) {
+      jgen.writeObject(entry);
+    }
+
+    jgen.writeEndArray();
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/fac84b3e/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONGeoValueDeserializer.java
----------------------------------------------------------------------
diff --git a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONGeoValueDeserializer.java b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONGeoValueDeserializer.java
new file mode 100644
index 0000000..f1863a7
--- /dev/null
+++ b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONGeoValueDeserializer.java
@@ -0,0 +1,274 @@
+/*
+ * 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 com.fasterxml.jackson.databind.JsonNode;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import org.apache.olingo.commons.api.Constants;
+import org.apache.olingo.commons.api.data.GeoUtils;
+import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException;
+import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
+import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
+import org.apache.olingo.commons.api.edm.geo.Geospatial;
+import org.apache.olingo.commons.api.edm.geo.GeospatialCollection;
+import org.apache.olingo.commons.api.edm.geo.LineString;
+import org.apache.olingo.commons.api.edm.geo.MultiLineString;
+import org.apache.olingo.commons.api.edm.geo.MultiPoint;
+import org.apache.olingo.commons.api.edm.geo.MultiPolygon;
+import org.apache.olingo.commons.api.edm.geo.Point;
+import org.apache.olingo.commons.api.edm.geo.Polygon;
+import org.apache.olingo.commons.core.edm.EdmTypeInfo;
+import org.apache.olingo.commons.core.edm.primitivetype.EdmDouble;
+
+class JSONGeoValueDeserializer {
+
+  private final ODataServiceVersion version;
+
+  public JSONGeoValueDeserializer(final ODataServiceVersion version) {
+    this.version = version;
+  }
+
+  private Point point(final Iterator<JsonNode> itor, final EdmPrimitiveTypeKind type, final String crs) {
+    Point point = null;
+
+    if (itor.hasNext()) {
+      point = new Point(GeoUtils.getDimension(type), crs);
+      try {
+        point.setX(EdmDouble.getInstance().valueOfString(itor.next().asText(), null, null,
+                Constants.DEFAULT_PRECISION, Constants.DEFAULT_SCALE, null, Double.class));
+        point.setY(EdmDouble.getInstance().valueOfString(itor.next().asText(), null, null,
+                Constants.DEFAULT_PRECISION, Constants.DEFAULT_SCALE, null, Double.class));
+      } catch (EdmPrimitiveTypeException e) {
+        throw new IllegalArgumentException("While deserializing point coordinates as double", e);
+      }
+    }
+
+    return point;
+  }
+
+  private MultiPoint multipoint(final Iterator<JsonNode> itor, final EdmPrimitiveTypeKind type,
+          final String crs) {
+
+    MultiPoint multiPoint = null;
+
+    if (itor.hasNext()) {
+      final List<Point> points = new ArrayList<Point>();
+      while (itor.hasNext()) {
+        final Iterator<JsonNode> mpItor = itor.next().elements();
+        points.add(point(mpItor, type, crs));
+      }
+      multiPoint = new MultiPoint(GeoUtils.getDimension(type), crs, points);
+    } else {
+      multiPoint = new MultiPoint(GeoUtils.getDimension(type), crs, Collections.<Point>emptyList());
+    }
+
+    return multiPoint;
+  }
+
+  private LineString lineString(final Iterator<JsonNode> itor, final EdmPrimitiveTypeKind type,
+          final String crs) {
+
+    LineString lineString = null;
+
+    if (itor.hasNext()) {
+      final List<Point> points = new ArrayList<Point>();
+      while (itor.hasNext()) {
+        final Iterator<JsonNode> mpItor = itor.next().elements();
+        points.add(point(mpItor, type, crs));
+      }
+      lineString = new LineString(GeoUtils.getDimension(type), crs, points);
+    } else {
+      lineString = new LineString(GeoUtils.getDimension(type), crs, Collections.<Point>emptyList());
+    }
+
+    return lineString;
+  }
+
+  private MultiLineString multiLineString(final Iterator<JsonNode> itor, final EdmPrimitiveTypeKind type,
+          final String crs) {
+
+    MultiLineString multiLineString = null;
+
+    if (itor.hasNext()) {
+      final List<LineString> lineStrings = new ArrayList<LineString>();
+      while (itor.hasNext()) {
+        final Iterator<JsonNode> mlsItor = itor.next().elements();
+        lineStrings.add(lineString(mlsItor, type, crs));
+      }
+      multiLineString = new MultiLineString(GeoUtils.getDimension(type), crs, lineStrings);
+    } else {
+      multiLineString = new MultiLineString(GeoUtils.getDimension(type), crs, Collections.<LineString>emptyList());
+    }
+
+    return multiLineString;
+  }
+
+  private Polygon polygon(final Iterator<JsonNode> itor, final EdmPrimitiveTypeKind type,
+          final String crs) {
+
+    List<Point> extPoints = null;
+    if (itor.hasNext()) {
+      final Iterator<JsonNode> extItor = itor.next().elements();
+      if (extItor.hasNext()) {
+        extPoints = new ArrayList<Point>();
+        while (extItor.hasNext()) {
+          final Iterator<JsonNode> mpItor = extItor.next().elements();
+          extPoints.add(point(mpItor, type, crs));
+        }
+      }
+    }
+
+    List<Point> intPoints = null;
+    if (itor.hasNext()) {
+      final Iterator<JsonNode> intItor = itor.next().elements();
+      if (intItor.hasNext()) {
+        intPoints = new ArrayList<Point>();
+        while (intItor.hasNext()) {
+          final Iterator<JsonNode> mpItor = intItor.next().elements();
+          intPoints.add(point(mpItor, type, crs));
+        }
+      }
+    }
+
+    return new Polygon(GeoUtils.getDimension(type), crs, intPoints, extPoints);
+  }
+
+  private MultiPolygon multiPolygon(final Iterator<JsonNode> itor, final EdmPrimitiveTypeKind type,
+          final String crs) {
+
+    MultiPolygon multiPolygon = null;
+
+    if (itor.hasNext()) {
+      final List<Polygon> polygons = new ArrayList<Polygon>();
+      while (itor.hasNext()) {
+        final Iterator<JsonNode> mpItor = itor.next().elements();
+        polygons.add(polygon(mpItor, type, crs));
+      }
+      multiPolygon = new MultiPolygon(GeoUtils.getDimension(type), crs, polygons);
+    } else {
+      multiPolygon = new MultiPolygon(GeoUtils.getDimension(type), crs, Collections.<Polygon>emptyList());
+    }
+
+    return multiPolygon;
+  }
+
+  private GeospatialCollection collection(final Iterator<JsonNode> itor, final EdmPrimitiveTypeKind type,
+          final String crs) {
+
+    GeospatialCollection collection = null;
+
+    if (itor.hasNext()) {
+      final List<Geospatial> geospatials = new ArrayList<Geospatial>();
+
+      while (itor.hasNext()) {
+        final JsonNode geo = itor.next();
+        final String collItemType = geo.get(Constants.ATTR_TYPE).asText();
+        final String callAsType;
+        if (EdmPrimitiveTypeKind.GeographyCollection.name().equals(collItemType)
+                || EdmPrimitiveTypeKind.GeometryCollection.name().equals(collItemType)) {
+
+          callAsType = collItemType;
+        } else {
+          callAsType = (type == EdmPrimitiveTypeKind.GeographyCollection ? "Geography" : "Geometry")
+                  + collItemType;
+        }
+
+        geospatials.add(deserialize(geo, new EdmTypeInfo.Builder().setTypeExpression(callAsType).build()));
+      }
+
+      collection = new GeospatialCollection(GeoUtils.getDimension(type), crs, geospatials);
+    } else {
+      collection = new GeospatialCollection(GeoUtils.getDimension(type), crs, Collections.<Geospatial>emptyList());
+    }
+
+    return collection;
+  }
+
+  public Geospatial deserialize(final JsonNode node, final EdmTypeInfo typeInfo) {
+    final EdmPrimitiveTypeKind actualType;
+    if ((typeInfo.getPrimitiveTypeKind() == EdmPrimitiveTypeKind.Geography
+            || typeInfo.getPrimitiveTypeKind() == EdmPrimitiveTypeKind.Geometry)
+            && node.has(Constants.ATTR_TYPE)) {
+
+      String nodeType = node.get(Constants.ATTR_TYPE).asText();
+      if (nodeType.startsWith("Geo")) {
+        final int yIdx = nodeType.indexOf('y');
+        nodeType = nodeType.substring(yIdx + 1);
+      }
+      actualType = EdmPrimitiveTypeKind.valueOfFQN(version, typeInfo.getFullQualifiedName().toString() + nodeType);
+    } else {
+      actualType = typeInfo.getPrimitiveTypeKind();
+    }
+
+    final Iterator<JsonNode> cooItor = node.has(Constants.JSON_COORDINATES)
+            ? node.get(Constants.JSON_COORDINATES).elements()
+            : Collections.<JsonNode>emptyList().iterator();
+
+    String crs = null;
+    if (node.has(Constants.JSON_CRS)) {
+      crs = node.get(Constants.JSON_CRS).get(Constants.PROPERTIES).get(Constants.JSON_NAME).asText().split(":")[1];
+    }
+
+    Geospatial value = null;
+    switch (actualType) {
+      case GeographyPoint:
+      case GeometryPoint:
+        value = point(cooItor, actualType, crs);
+        break;
+
+      case GeographyMultiPoint:
+      case GeometryMultiPoint:
+        value = multipoint(cooItor, actualType, crs);
+        break;
+
+      case GeographyLineString:
+      case GeometryLineString:
+        value = lineString(cooItor, actualType, crs);
+        break;
+
+      case GeographyMultiLineString:
+      case GeometryMultiLineString:
+        value = multiLineString(cooItor, actualType, crs);
+        break;
+
+      case GeographyPolygon:
+      case GeometryPolygon:
+        value = polygon(cooItor, actualType, crs);
+        break;
+
+      case GeographyMultiPolygon:
+      case GeometryMultiPolygon:
+        value = multiPolygon(cooItor, actualType, crs);
+        break;
+
+      case GeographyCollection:
+      case GeometryCollection:
+        value = collection(node.get(Constants.JSON_GEOMETRIES).elements(), actualType, crs);
+        break;
+
+      default:
+    }
+
+    return value;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/fac84b3e/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONGeoValueSerializer.java
----------------------------------------------------------------------
diff --git a/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONGeoValueSerializer.java b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONGeoValueSerializer.java
new file mode 100644
index 0000000..95c30d4
--- /dev/null
+++ b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONGeoValueSerializer.java
@@ -0,0 +1,183 @@
+/*
+ * 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 com.fasterxml.jackson.core.JsonGenerator;
+import java.io.IOException;
+import java.util.Iterator;
+import org.apache.olingo.commons.api.Constants;
+import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException;
+import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
+import org.apache.olingo.commons.api.edm.geo.ComposedGeospatial;
+import org.apache.olingo.commons.api.edm.geo.Geospatial;
+import org.apache.olingo.commons.api.edm.geo.GeospatialCollection;
+import org.apache.olingo.commons.api.edm.geo.LineString;
+import org.apache.olingo.commons.api.edm.geo.MultiLineString;
+import org.apache.olingo.commons.api.edm.geo.MultiPoint;
+import org.apache.olingo.commons.api.edm.geo.MultiPolygon;
+import org.apache.olingo.commons.api.edm.geo.Point;
+import org.apache.olingo.commons.api.edm.geo.Polygon;
+import org.apache.olingo.commons.core.edm.primitivetype.EdmDouble;
+
+class JSONGeoValueSerializer {
+
+  private void crs(final JsonGenerator jgen, final String crs) throws IOException {
+    jgen.writeObjectFieldStart(Constants.JSON_CRS);
+    jgen.writeStringField(Constants.ATTR_TYPE, Constants.JSON_NAME);
+    jgen.writeObjectFieldStart(Constants.PROPERTIES);
+    jgen.writeStringField(Constants.JSON_NAME, "EPSG:" + crs);
+    jgen.writeEndObject();
+    jgen.writeEndObject();
+  }
+
+  private void point(final JsonGenerator jgen, final Point point) throws IOException {
+    try {
+      jgen.writeNumber(EdmDouble.getInstance().valueToString(point.getX(), null, null,
+              Constants.DEFAULT_PRECISION, Constants.DEFAULT_SCALE, null));
+      jgen.writeNumber(EdmDouble.getInstance().valueToString(point.getY(), null, null,
+              Constants.DEFAULT_PRECISION, Constants.DEFAULT_SCALE, null));
+    } catch (EdmPrimitiveTypeException e) {
+      throw new IllegalArgumentException("While serializing point coordinates as double", e);
+    }
+  }
+
+  private void multipoint(final JsonGenerator jgen, final MultiPoint multiPoint) throws IOException {
+    for (final Iterator<Point> itor = multiPoint.iterator(); itor.hasNext();) {
+      jgen.writeStartArray();
+      point(jgen, itor.next());
+      jgen.writeEndArray();
+    }
+  }
+
+  private void lineString(final JsonGenerator jgen, final ComposedGeospatial<Point> lineString) throws IOException {
+    for (final Iterator<Point> itor = lineString.iterator(); itor.hasNext();) {
+      jgen.writeStartArray();
+      point(jgen, itor.next());
+      jgen.writeEndArray();
+    }
+  }
+
+  private void multiLineString(final JsonGenerator jgen, final MultiLineString multiLineString) throws IOException {
+    for (final Iterator<LineString> itor = multiLineString.iterator(); itor.hasNext();) {
+      jgen.writeStartArray();
+      lineString(jgen, itor.next());
+      jgen.writeEndArray();
+    }
+  }
+
+  private void polygon(final JsonGenerator jgen, final Polygon polygon) throws IOException {
+    if (!polygon.getExterior().isEmpty()) {
+      jgen.writeStartArray();
+      lineString(jgen, polygon.getExterior());
+      jgen.writeEndArray();
+    }
+    if (!polygon.getInterior().isEmpty()) {
+      jgen.writeStartArray();
+      lineString(jgen, polygon.getInterior());
+      jgen.writeEndArray();
+    }
+  }
+
+  private void multiPolygon(final JsonGenerator jgen, final MultiPolygon multiPolygon) throws IOException {
+    for (final Iterator<Polygon> itor = multiPolygon.iterator(); itor.hasNext();) {
+      final Polygon polygon = itor.next();
+      jgen.writeStartArray();
+      polygon(jgen, polygon);
+      jgen.writeEndArray();
+    }
+  }
+
+  private void collection(final JsonGenerator jgen, final GeospatialCollection collection) throws IOException {
+    jgen.writeArrayFieldStart(Constants.JSON_GEOMETRIES);
+    for (final Iterator<Geospatial> itor = collection.iterator(); itor.hasNext();) {
+      jgen.writeStartObject();
+      serialize(jgen, itor.next());
+      jgen.writeEndObject();
+    }
+    jgen.writeEndArray();
+  }
+
+  public void serialize(final JsonGenerator jgen, final Geospatial value) throws IOException {
+    if (value.getEdmPrimitiveTypeKind().equals(EdmPrimitiveTypeKind.GeographyCollection)
+            || value.getEdmPrimitiveTypeKind().equals(EdmPrimitiveTypeKind.GeometryCollection)) {
+
+      jgen.writeStringField(Constants.ATTR_TYPE, EdmPrimitiveTypeKind.GeometryCollection.name());
+    } else {
+      final int yIdx = value.getEdmPrimitiveTypeKind().name().indexOf('y');
+      final String itemType = value.getEdmPrimitiveTypeKind().name().substring(yIdx + 1);
+      jgen.writeStringField(Constants.ATTR_TYPE, itemType);
+    }
+
+    switch (value.getEdmPrimitiveTypeKind()) {
+      case GeographyPoint:
+      case GeometryPoint:
+        jgen.writeArrayFieldStart(Constants.JSON_COORDINATES);
+        point(jgen, (Point) value);
+        jgen.writeEndArray();
+        break;
+
+      case GeographyMultiPoint:
+      case GeometryMultiPoint:
+        jgen.writeArrayFieldStart(Constants.JSON_COORDINATES);
+        multipoint(jgen, (MultiPoint) value);
+        jgen.writeEndArray();
+        break;
+
+      case GeographyLineString:
+      case GeometryLineString:
+        jgen.writeArrayFieldStart(Constants.JSON_COORDINATES);
+        lineString(jgen, (LineString) value);
+        jgen.writeEndArray();
+        break;
+
+      case GeographyMultiLineString:
+      case GeometryMultiLineString:
+        jgen.writeArrayFieldStart(Constants.JSON_COORDINATES);
+        multiLineString(jgen, (MultiLineString) value);
+        jgen.writeEndArray();
+        break;
+
+      case GeographyPolygon:
+      case GeometryPolygon:
+        jgen.writeArrayFieldStart(Constants.JSON_COORDINATES);
+        polygon(jgen, (Polygon) value);
+        jgen.writeEndArray();
+        break;
+
+      case GeographyMultiPolygon:
+      case GeometryMultiPolygon:
+        jgen.writeArrayFieldStart(Constants.JSON_COORDINATES);
+        multiPolygon(jgen, (MultiPolygon) value);
+        jgen.writeEndArray();
+        break;
+
+      case GeographyCollection:
+      case GeometryCollection:
+        collection(jgen, (GeospatialCollection) value);
+        break;
+
+      default:
+    }
+
+    if (value.getCrs() != null) {
+      crs(jgen, value.getCrs());
+    }
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/fac84b3e/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
new file mode 100644
index 0000000..b68d998
--- /dev/null
+++ b/lib/commons-core/src/main/java/org/apache/olingo/commons/core/data/JSONPropertyDeserializer.java
@@ -0,0 +1,71 @@
+/*
+ * 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 com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import java.io.IOException;
+import java.net.URI;
+import org.apache.olingo.commons.api.Constants;
+
+/**
+ * Parse JSON string into <tt>JSONPropertyImpl</tt>.
+ *
+ * @see JSONPropertyImpl
+ */
+public class JSONPropertyDeserializer extends AbstractJsonDeserializer<JSONPropertyImpl> {
+
+  @Override
+  protected JSONPropertyImpl doDeserialize(final JsonParser parser, final DeserializationContext ctxt)
+          throws IOException, JsonProcessingException {
+
+    final ObjectNode tree = (ObjectNode) parser.getCodec().readTree(parser);
+
+    final JSONPropertyImpl property = new JSONPropertyImpl();
+
+    if (tree.hasNonNull(Constants.JSON_METADATA)) {
+      property.setMetadata(URI.create(tree.get(Constants.JSON_METADATA).textValue()));
+      tree.remove(Constants.JSON_METADATA);
+    }
+
+    if (property.getMetadata() != null) {
+      final String metadataURI = property.getMetadata().toASCIIString();
+      final int dashIdx = metadataURI.lastIndexOf('#');
+      if (dashIdx != -1) {
+        property.setType(metadataURI.substring(dashIdx + 1));
+      }
+    }
+
+    if (tree.has(Constants.JSON_TYPE) && property.getType() == null) {
+      property.setType(tree.get(Constants.JSON_TYPE).asText());
+    }
+
+    if (tree.has(Constants.JSON_NULL) && tree.get(Constants.JSON_NULL).asBoolean()) {
+      property.setValue(new NullValueImpl());
+    }
+
+    if (property.getValue() == null) {
+      value(property, tree.has(Constants.JSON_VALUE) ? tree.get(Constants.JSON_VALUE) : tree);
+    }
+
+    return property;
+  }
+}