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/11 13:53:11 UTC

[09/11] [OLINGO-200] V3 (de)serializers + unit tests merged

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/78c3eaa4/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AbstractEntry.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AbstractEntry.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AbstractEntry.java
new file mode 100644
index 0000000..fc65cb7
--- /dev/null
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AbstractEntry.java
@@ -0,0 +1,179 @@
+/*
+ * 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.client.core.data;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.olingo.client.api.data.Entry;
+import org.apache.olingo.client.api.data.Link;
+import org.apache.olingo.client.api.data.Operation;
+import org.w3c.dom.Element;
+
+/**
+ * Abstract base for classes implementing an OData entry in Atom and JSON.
+ */
+public abstract class AbstractEntry extends AbstractPayloadObject implements Entry {
+
+  private static final long serialVersionUID = 2127764552600969783L;
+
+  private String eTag;
+
+  private String type;
+
+  private String id;
+
+  private Link readLink;
+
+  private Link editLink;
+
+  private final List<Link> associationLinks = new ArrayList<Link>();
+
+  private final List<Link> navigationLinks = new ArrayList<Link>();
+
+  private final List<Link> mediaEditLinks = new ArrayList<Link>();
+
+  private final List<Operation> operations = new ArrayList<Operation>();
+
+  private Element content;
+
+  private Element mediaEntryProperties;
+
+  private String mediaContentSource;
+
+  private String mediaContentType;
+
+  @Override
+  public String getETag() {
+    return eTag;
+  }
+
+  @Override
+  public void setETag(final String eTag) {
+    this.eTag = eTag;
+  }
+
+  @Override
+  public String getType() {
+    return type;
+  }
+
+  @Override
+  public void setType(final String type) {
+    this.type = type;
+  }
+
+  @Override
+  public String getId() {
+    return id;
+  }
+
+  @Override
+  public void setId(final String id) {
+    this.id = id;
+  }
+
+  @Override
+  public Link getSelfLink() {
+    return readLink;
+  }
+
+  @Override
+  public void setSelfLink(final Link readLink) {
+    this.readLink = readLink;
+  }
+
+  @Override
+  public Link getEditLink() {
+    return editLink;
+  }
+
+  @Override
+  public void setEditLink(final Link editLink) {
+    this.editLink = editLink;
+  }
+
+  @Override
+  public List<Link> getAssociationLinks() {
+    return associationLinks;
+  }
+
+  @Override
+  public List<Link> getNavigationLinks() {
+    return navigationLinks;
+  }
+
+  @Override
+  public List<Link> getMediaEditLinks() {
+    return mediaEditLinks;
+  }
+
+  @Override
+  public List<Operation> getOperations() {
+    return operations;
+  }
+
+  @Override
+  public Element getContent() {
+    return content;
+  }
+
+  @Override
+  public void setContent(final Element content) {
+    this.content = content;
+  }
+
+  /**
+   * {@inheritDoc }
+   */
+  @Override
+  public Element getMediaEntryProperties() {
+    return mediaEntryProperties;
+  }
+
+  @Override
+  public void setMediaEntryProperties(final Element mediaEntryProperties) {
+    this.mediaEntryProperties = mediaEntryProperties;
+  }
+
+  @Override
+  public String getMediaContentType() {
+    return this.mediaContentType;
+  }
+
+  @Override
+  public void setMediaContentType(final String mediaContentType) {
+    this.mediaContentType = mediaContentType;
+  }
+
+  @Override
+  public String getMediaContentSource() {
+    return this.mediaContentSource;
+  }
+
+  @Override
+  public void setMediaContentSource(final String mediaContentSource) {
+    this.mediaContentSource = mediaContentSource;
+  }
+
+  @Override
+  public boolean isMediaEntry() {
+    return getMediaEntryProperties() != null || StringUtils.isNotBlank(this.mediaContentSource);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/78c3eaa4/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AbstractPayloadObject.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AbstractPayloadObject.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AbstractPayloadObject.java
new file mode 100644
index 0000000..b707c49
--- /dev/null
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AbstractPayloadObject.java
@@ -0,0 +1,48 @@
+/*
+ * 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.client.core.data;
+
+import java.io.Serializable;
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
+import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+/**
+ * Abstract representation of a payload (Atom, JSON) object.
+ */
+public abstract class AbstractPayloadObject implements Serializable {
+
+  private static final long serialVersionUID = 1634654241914156675L;
+
+  @Override
+  public boolean equals(final Object obj) {
+    return EqualsBuilder.reflectionEquals(this, obj);
+  }
+
+  @Override
+  public int hashCode() {
+    return HashCodeBuilder.reflectionHashCode(this);
+  }
+
+  @Override
+  public String toString() {
+    return ReflectionToStringBuilder.toString(this, ToStringStyle.MULTI_LINE_STYLE);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/78c3eaa4/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AtomDeserializer.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AtomDeserializer.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AtomDeserializer.java
new file mode 100644
index 0000000..db18bfa
--- /dev/null
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AtomDeserializer.java
@@ -0,0 +1,217 @@
+/*
+ * 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.client.core.data;
+
+import com.fasterxml.jackson.databind.util.ISO8601DateFormat;
+import java.net.URI;
+import java.util.List;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.olingo.client.api.ODataClient;
+import org.apache.olingo.client.api.ODataConstants;
+import org.apache.olingo.client.api.utils.XMLUtils;
+import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+public class AtomDeserializer {
+
+  private static final Logger LOG = LoggerFactory.getLogger(AtomDeserializer.class);
+
+  private static final ISO8601DateFormat ISO_DATEFORMAT = new ISO8601DateFormat();
+
+  private final ODataClient client;
+
+  public AtomDeserializer(final ODataClient client) {
+    this.client = client;
+  }
+
+  private void common(final Element input, final AtomObject object) {
+    if (StringUtils.isNotBlank(input.getAttribute(ODataConstants.ATTR_XMLBASE))) {
+      object.setBaseURI(input.getAttribute(ODataConstants.ATTR_XMLBASE));
+    }
+
+    final List<Element> ids = XMLUtils.getChildElements(input, ODataConstants.ATOM_ELEM_ID);
+    if (!ids.isEmpty()) {
+      object.setId(ids.get(0).getTextContent());
+    }
+
+    final List<Element> titles = XMLUtils.getChildElements(input, ODataConstants.ATOM_ELEM_TITLE);
+    if (!titles.isEmpty()) {
+      object.setTitle(titles.get(0).getTextContent());
+    }
+
+    final List<Element> summaries = XMLUtils.getChildElements(input, ODataConstants.ATOM_ELEM_SUMMARY);
+    if (!summaries.isEmpty()) {
+      object.setSummary(summaries.get(0).getTextContent());
+    }
+
+    final List<Element> updateds = XMLUtils.getChildElements(input, ODataConstants.ATOM_ELEM_UPDATED);
+    if (!updateds.isEmpty()) {
+      try {
+        object.setUpdated(ISO_DATEFORMAT.parse(updateds.get(0).getTextContent()));
+      } catch (Exception e) {
+        LOG.error("Could not parse date {}", updateds.get(0).getTextContent(), e);
+      }
+    }
+  }
+
+  public AtomEntryImpl entry(final Element input) {
+    if (!ODataConstants.ATOM_ELEM_ENTRY.equals(input.getNodeName())) {
+      return null;
+    }
+
+    final AtomEntryImpl entry = new AtomEntryImpl();
+
+    common(input, entry);
+
+    final String etag = input.getAttribute(ODataConstants.ATOM_ATTR_ETAG);
+    if (StringUtils.isNotBlank(etag)) {
+      entry.setETag(etag);
+    }
+
+    final List<Element> categories = XMLUtils.getChildElements(input, ODataConstants.ATOM_ELEM_CATEGORY);
+    if (!categories.isEmpty()) {
+      entry.setType(categories.get(0).getAttribute(ODataConstants.ATOM_ATTR_TERM));
+    }
+
+    final List<Element> links = XMLUtils.getChildElements(input, ODataConstants.ATOM_ELEM_LINK);
+    for (Element linkElem : links) {
+      final LinkImpl link = new LinkImpl();
+      link.setRel(linkElem.getAttribute(ODataConstants.ATTR_REL));
+      link.setTitle(linkElem.getAttribute(ODataConstants.ATTR_TITLE));
+      link.setHref(linkElem.getAttribute(ODataConstants.ATTR_HREF));
+
+      if (ODataConstants.SELF_LINK_REL.equals(link.getRel())) {
+        entry.setSelfLink(link);
+      } else if (ODataConstants.EDIT_LINK_REL.equals(link.getRel())) {
+        entry.setEditLink(link);
+      } else if (link.getRel().startsWith(
+              client.getServiceVersion().getNamespaceMap().get(ODataServiceVersion.NAVIGATION_LINK_REL))) {
+
+        link.setType(linkElem.getAttribute(ODataConstants.ATTR_TYPE));
+        entry.getNavigationLinks().add(link);
+
+        final List<Element> inlines = XMLUtils.getChildElements(linkElem, ODataConstants.ATOM_ELEM_INLINE);
+        if (!inlines.isEmpty()) {
+          final List<Element> entries =
+                  XMLUtils.getChildElements(inlines.get(0), ODataConstants.ATOM_ELEM_ENTRY);
+          if (!entries.isEmpty()) {
+            link.setInlineEntry(entry(entries.get(0)));
+          }
+
+          final List<Element> feeds =
+                  XMLUtils.getChildElements(inlines.get(0), ODataConstants.ATOM_ELEM_FEED);
+          if (!feeds.isEmpty()) {
+            link.setInlineFeed(feed(feeds.get(0)));
+          }
+        }
+      } else if (link.getRel().startsWith(
+              client.getServiceVersion().getNamespaceMap().get(ODataServiceVersion.ASSOCIATION_LINK_REL))) {
+
+        entry.getAssociationLinks().add(link);
+      } else if (link.getRel().startsWith(
+              client.getServiceVersion().getNamespaceMap().get(ODataServiceVersion.MEDIA_EDIT_LINK_REL))) {
+
+        entry.getMediaEditLinks().add(link);
+      }
+    }
+
+    final List<Element> authors = XMLUtils.getChildElements(input, ODataConstants.ATOM_ELEM_AUTHOR);
+    if (!authors.isEmpty()) {
+      final AtomEntryImpl.Author author = new AtomEntryImpl.Author();
+      for (Node child : XMLUtils.getChildNodes(input, Node.ELEMENT_NODE)) {
+        if (ODataConstants.ATOM_ELEM_AUTHOR_NAME.equals(XMLUtils.getSimpleName(child))) {
+          author.setName(child.getTextContent());
+        } else if (ODataConstants.ATOM_ELEM_AUTHOR_URI.equals(XMLUtils.getSimpleName(child))) {
+          author.setUri(child.getTextContent());
+        } else if (ODataConstants.ATOM_ELEM_AUTHOR_EMAIL.equals(XMLUtils.getSimpleName(child))) {
+          author.setEmail(child.getTextContent());
+        }
+      }
+      if (!author.isEmpty()) {
+        entry.setAuthor(author);
+      }
+    }
+
+    final List<Element> actions = XMLUtils.getChildElements(input, ODataConstants.ATOM_ELEM_ACTION);
+    for (Element action : actions) {
+      final OperationImpl operation = new OperationImpl();
+      operation.setMetadataAnchor(action.getAttribute(ODataConstants.ATTR_METADATA));
+      operation.setTitle(action.getAttribute(ODataConstants.ATTR_TITLE));
+      operation.setTarget(URI.create(action.getAttribute(ODataConstants.ATTR_TARGET)));
+
+      entry.getOperations().add(operation);
+    }
+
+    final List<Element> contents = XMLUtils.getChildElements(input, ODataConstants.ATOM_ELEM_CONTENT);
+    if (!contents.isEmpty()) {
+      final Element content = contents.get(0);
+
+      List<Element> props = XMLUtils.getChildElements(content, ODataConstants.ELEM_PROPERTIES);
+      if (props.isEmpty()) {
+        entry.setMediaContentSource(content.getAttribute(ODataConstants.ATOM_ATTR_SRC));
+        entry.setMediaContentType(content.getAttribute(ODataConstants.ATTR_TYPE));
+
+        props = XMLUtils.getChildElements(input, ODataConstants.ELEM_PROPERTIES);
+        if (!props.isEmpty()) {
+          entry.setMediaEntryProperties(props.get(0));
+        }
+      } else {
+        entry.setContent(props.get(0));
+      }
+    }
+
+    return entry;
+  }
+
+  public AtomFeedImpl feed(final Element input) {
+    if (!ODataConstants.ATOM_ELEM_FEED.equals(input.getNodeName())) {
+      return null;
+    }
+
+    final AtomFeedImpl feed = new AtomFeedImpl();
+
+    common(input, feed);
+
+    final List<Element> entries = XMLUtils.getChildElements(input, ODataConstants.ATOM_ELEM_ENTRY);
+    for (Element entry : entries) {
+      feed.getEntries().add(entry(entry));
+    }
+
+    final List<Element> links = XMLUtils.getChildElements(input, ODataConstants.ATOM_ELEM_LINK);
+    for (Element link : links) {
+      if (ODataConstants.NEXT_LINK_REL.equals(link.getAttribute(ODataConstants.ATTR_REL))) {
+        feed.setNext(URI.create(link.getAttribute(ODataConstants.ATTR_HREF)));
+      }
+    }
+
+    final List<Element> counts = XMLUtils.getChildElements(input, ODataConstants.ATOM_ATTR_COUNT);
+    if (!counts.isEmpty()) {
+      try {
+        feed.setCount(Integer.parseInt(counts.get(0).getTextContent()));
+      } catch (Exception e) {
+        LOG.error("Could not parse $inlinecount {}", counts.get(0).getTextContent(), e);
+      }
+    }
+
+    return feed;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/78c3eaa4/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AtomEntryImpl.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AtomEntryImpl.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AtomEntryImpl.java
new file mode 100644
index 0000000..5ddb182
--- /dev/null
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AtomEntryImpl.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.client.core.data;
+
+import java.net.URI;
+import java.util.Date;
+import org.apache.commons.lang3.StringUtils;
+
+public class AtomEntryImpl extends AbstractEntry implements AtomObject {
+
+  private static final long serialVersionUID = 6973729343868293279L;
+
+  public static class Author {
+
+    private String name;
+
+    private String uri;
+
+    private String email;
+
+    public String getName() {
+      return name;
+    }
+
+    public void setName(final String name) {
+      this.name = name;
+    }
+
+    public String getUri() {
+      return uri;
+    }
+
+    public void setUri(final String uri) {
+      this.uri = uri;
+    }
+
+    public String getEmail() {
+      return email;
+    }
+
+    public void setEmail(final String email) {
+      this.email = email;
+    }
+
+    public boolean isEmpty() {
+      return StringUtils.isBlank(name) && StringUtils.isBlank(uri) && StringUtils.isBlank(email);
+    }
+  }
+
+  private URI baseURI;
+
+  private String title;
+
+  private String summary;
+
+  private Date updated;
+
+  private Author author;
+
+  @Override
+  public void setBaseURI(final String baseURI) {
+    this.baseURI = URI.create(baseURI);
+  }
+
+  @Override
+  public URI getBaseURI() {
+    return baseURI;
+  }
+
+  public String getTitle() {
+    return title;
+  }
+
+  @Override
+  public void setTitle(final String title) {
+    this.title = title;
+  }
+
+  public String getSummary() {
+    return summary;
+  }
+
+  @Override
+  public void setSummary(final String summary) {
+    this.summary = summary;
+  }
+
+  public Date getUpdated() {
+    return new Date(updated.getTime());
+  }
+
+  @Override
+  public void setUpdated(final Date updated) {
+    this.updated = new Date(updated.getTime());
+  }
+
+  public Author getAuthor() {
+    return author;
+  }
+
+  public void setAuthor(final Author author) {
+    this.author = author;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/78c3eaa4/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AtomFeedImpl.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AtomFeedImpl.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AtomFeedImpl.java
new file mode 100644
index 0000000..08a85db
--- /dev/null
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AtomFeedImpl.java
@@ -0,0 +1,122 @@
+/*
+ * 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.client.core.data;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import org.apache.olingo.client.api.data.Entry;
+import org.apache.olingo.client.api.data.Feed;
+
+/**
+ * List of entries, represented via Atom.
+ *
+ * @see AtomEntry
+ */
+public class AtomFeedImpl extends AbstractPayloadObject implements AtomObject, Feed {
+
+  private static final long serialVersionUID = 5466590540021319153L;
+
+  private URI baseURI;
+
+  private String id;
+
+  private String title;
+
+  private String summary;
+
+  private Date updated;
+
+  private Integer count;
+
+  private final List<Entry> entries = new ArrayList<Entry>();
+
+  private URI next;
+
+  @Override
+  public URI getBaseURI() {
+    return baseURI;
+  }
+
+  @Override
+  public void setBaseURI(final String baseURI) {
+    this.baseURI = URI.create(baseURI);
+  }
+
+  public String getId() {
+    return id;
+  }
+
+  @Override
+  public void setId(final String id) {
+    this.id = id;
+  }
+
+  public String getTitle() {
+    return title;
+  }
+
+  @Override
+  public void setTitle(final String title) {
+    this.title = title;
+  }
+
+  public String getSummary() {
+    return summary;
+  }
+
+  @Override
+  public void setSummary(final String summary) {
+    this.summary = summary;
+  }
+
+  public Date getUpdated() {
+    return new Date(updated.getTime());
+  }
+
+  @Override
+  public void setUpdated(final Date updated) {
+    this.updated = new Date(updated.getTime());
+  }
+
+  public void setCount(final Integer count) {
+    this.count = count;
+  }
+
+  @Override
+  public Integer getCount() {
+    return count;
+  }
+
+  @Override
+  public List<Entry> getEntries() {
+    return entries;
+  }
+
+  @Override
+  public void setNext(final URI next) {
+    this.next = next;
+  }
+
+  @Override
+  public URI getNext() {
+    return next;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/78c3eaa4/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AtomObject.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AtomObject.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AtomObject.java
new file mode 100644
index 0000000..b0ecab9
--- /dev/null
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AtomObject.java
@@ -0,0 +1,41 @@
+/*
+ * 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.client.core.data;
+
+import java.util.Date;
+
+/**
+ * Common methods for <tt>AtomEntryImpl</tt> and <tt>AtomFeedImpl</tt>.
+ *
+ * @see AtomEntryImpl
+ * @see AtomFeedImpl
+ */
+public interface AtomObject {
+
+  void setBaseURI(String baseURI);
+
+  void setId(String id);
+
+  void setTitle(String title);
+
+  void setSummary(String summary);
+
+  void setUpdated(Date updated);
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/78c3eaa4/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AtomSerializer.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AtomSerializer.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AtomSerializer.java
new file mode 100644
index 0000000..73def31
--- /dev/null
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/AtomSerializer.java
@@ -0,0 +1,184 @@
+/*
+ * 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.client.core.data;
+
+import java.util.List;
+import javax.xml.XMLConstants;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.ParserConfigurationException;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.http.entity.ContentType;
+import org.apache.olingo.client.api.ODataClient;
+import org.apache.olingo.client.api.ODataConstants;
+import org.apache.olingo.client.api.data.Entry;
+import org.apache.olingo.client.api.data.Link;
+import org.apache.olingo.client.api.utils.XMLUtils;
+import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+public class AtomSerializer {
+
+  private final ODataClient client;
+
+  public AtomSerializer(final ODataClient client) {
+    this.client = client;
+  }
+
+  public <T extends AbstractPayloadObject> Element serialize(final T obj) throws ParserConfigurationException {
+    if (obj instanceof AtomEntryImpl) {
+      return entry((AtomEntryImpl) obj);
+    } else if (obj instanceof AtomFeedImpl) {
+      return feed((AtomFeedImpl) obj);
+    } else {
+      throw new IllegalArgumentException("Unsupported Atom object for standalone serialization: " + obj);
+    }
+  }
+
+  private void setLinks(final Element entry, final List<Link> links) throws ParserConfigurationException {
+    for (Link link : links) {
+      final Element linkElem = entry.getOwnerDocument().createElement(ODataConstants.ATOM_ELEM_LINK);
+
+      linkElem.setAttribute(ODataConstants.ATTR_REL, link.getRel());
+      linkElem.setAttribute(ODataConstants.ATTR_TITLE, link.getTitle());
+      linkElem.setAttribute(ODataConstants.ATTR_HREF, link.getHref());
+
+      if (StringUtils.isNotBlank(link.getType())) {
+        linkElem.setAttribute(ODataConstants.ATTR_TYPE, link.getType());
+      }
+
+      if (link.getInlineEntry() != null || link.getInlineFeed() != null) {
+        final Element inline = entry.getOwnerDocument().createElement(ODataConstants.ATOM_ELEM_INLINE);
+        linkElem.appendChild(inline);
+
+        if (link.getInlineEntry() != null) {
+          inline.appendChild(entry.getOwnerDocument().importNode(
+                  entry((AtomEntryImpl) link.getInlineEntry()), true));
+        }
+        if (link.getInlineFeed() != null) {
+          inline.appendChild(entry.getOwnerDocument().importNode(
+                  feed((AtomFeedImpl) link.getInlineFeed()), true));
+        }
+      }
+
+      entry.appendChild(linkElem);
+    }
+  }
+
+  private Element entry(final AtomEntryImpl entry) throws ParserConfigurationException {
+    final DocumentBuilder builder = XMLUtils.DOC_BUILDER_FACTORY.newDocumentBuilder();
+    final Document doc = builder.newDocument();
+
+    final Element entryElem = doc.createElement(ODataConstants.ATOM_ELEM_ENTRY);
+    entryElem.setAttribute(XMLConstants.XMLNS_ATTRIBUTE, ODataConstants.NS_ATOM);
+    entryElem.setAttribute(ODataConstants.XMLNS_METADATA,
+            client.getServiceVersion().getNamespaceMap().get(ODataServiceVersion.NS_METADATA));
+    entryElem.setAttribute(ODataConstants.XMLNS_DATASERVICES,
+            client.getServiceVersion().getNamespaceMap().get(ODataServiceVersion.NS_DATASERVICES));
+    entryElem.setAttribute(ODataConstants.XMLNS_GML, ODataConstants.NS_GML);
+    entryElem.setAttribute(ODataConstants.XMLNS_GEORSS, ODataConstants.NS_GEORSS);
+    if (entry.getBaseURI() != null) {
+      entryElem.setAttribute(ODataConstants.ATTR_XMLBASE, entry.getBaseURI().toASCIIString());
+    }
+    doc.appendChild(entryElem);
+
+    final Element category = doc.createElement(ODataConstants.ATOM_ELEM_CATEGORY);
+    category.setAttribute(ODataConstants.ATOM_ATTR_TERM, entry.getType());
+    category.setAttribute(ODataConstants.ATOM_ATTR_SCHEME,
+            client.getServiceVersion().getNamespaceMap().get(ODataServiceVersion.NS_SCHEME));
+    entryElem.appendChild(category);
+
+    if (StringUtils.isNotBlank(entry.getTitle())) {
+      final Element title = doc.createElement(ODataConstants.ATOM_ELEM_TITLE);
+      title.appendChild(doc.createTextNode(entry.getTitle()));
+      entryElem.appendChild(title);
+    }
+
+    if (StringUtils.isNotBlank(entry.getSummary())) {
+      final Element summary = doc.createElement(ODataConstants.ATOM_ELEM_SUMMARY);
+      summary.appendChild(doc.createTextNode(entry.getSummary()));
+      entryElem.appendChild(summary);
+    }
+
+    setLinks(entryElem, entry.getAssociationLinks());
+    setLinks(entryElem, entry.getNavigationLinks());
+    setLinks(entryElem, entry.getMediaEditLinks());
+
+    final Element content = doc.createElement(ODataConstants.ATOM_ELEM_CONTENT);
+    if (entry.isMediaEntry()) {
+      if (StringUtils.isNotBlank(entry.getMediaContentType())) {
+        content.setAttribute(ODataConstants.ATTR_TYPE, entry.getMediaContentType());
+      }
+      if (StringUtils.isNotBlank(entry.getMediaContentSource())) {
+        content.setAttribute(ODataConstants.ATOM_ATTR_SRC, entry.getMediaContentSource());
+      }
+      if (content.getAttributes().getLength() > 0) {
+        entryElem.appendChild(content);
+      }
+
+      if (entry.getMediaEntryProperties() != null) {
+        entryElem.appendChild(doc.importNode(entry.getMediaEntryProperties(), true));
+      }
+    } else {
+      content.setAttribute(ODataConstants.ATTR_TYPE, ContentType.APPLICATION_XML.getMimeType());
+      if (entry.getContent() != null) {
+        content.appendChild(doc.importNode(entry.getContent(), true));
+      }
+      entryElem.appendChild(content);
+    }
+
+    return entryElem;
+  }
+
+  private Element feed(final AtomFeedImpl feed) throws ParserConfigurationException {
+    final DocumentBuilder builder = XMLUtils.DOC_BUILDER_FACTORY.newDocumentBuilder();
+    final Document doc = builder.newDocument();
+
+    final Element feedElem = doc.createElement(ODataConstants.ATOM_ELEM_FEED);
+    feedElem.setAttribute(XMLConstants.XMLNS_ATTRIBUTE, ODataConstants.NS_ATOM);
+    feedElem.setAttribute(ODataConstants.XMLNS_METADATA,
+            client.getServiceVersion().getNamespaceMap().get(ODataServiceVersion.NS_METADATA));
+    feedElem.setAttribute(ODataConstants.XMLNS_DATASERVICES,
+            client.getServiceVersion().getNamespaceMap().get(ODataServiceVersion.NS_DATASERVICES));
+    feedElem.setAttribute(ODataConstants.XMLNS_GML, ODataConstants.NS_GML);
+    feedElem.setAttribute(ODataConstants.XMLNS_GEORSS, ODataConstants.NS_GEORSS);
+    if (feed.getBaseURI() != null) {
+      feedElem.setAttribute(ODataConstants.ATTR_XMLBASE, feed.getBaseURI().toASCIIString());
+    }
+    doc.appendChild(feedElem);
+
+    if (StringUtils.isNotBlank(feed.getTitle())) {
+      final Element title = doc.createElement(ODataConstants.ATOM_ELEM_TITLE);
+      title.appendChild(doc.createTextNode(feed.getTitle()));
+      feedElem.appendChild(title);
+    }
+
+    if (StringUtils.isNotBlank(feed.getSummary())) {
+      final Element summary = doc.createElement(ODataConstants.ATOM_ELEM_SUMMARY);
+      summary.appendChild(doc.createTextNode(feed.getSummary()));
+      feedElem.appendChild(summary);
+    }
+
+    for (Entry entry : feed.getEntries()) {
+      feedElem.appendChild(doc.importNode(entry((AtomEntryImpl) entry), true));
+    }
+
+    return feedElem;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/78c3eaa4/lib/client-core/src/main/java/org/apache/olingo/client/core/data/GeospatialJSONHandler.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/data/GeospatialJSONHandler.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/GeospatialJSONHandler.java
new file mode 100644
index 0000000..610f1a2
--- /dev/null
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/GeospatialJSONHandler.java
@@ -0,0 +1,412 @@
+/*
+ * 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.client.core.data;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JsonNode;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import javax.xml.parsers.DocumentBuilder;
+import org.apache.olingo.client.api.ODataClient;
+import org.apache.olingo.client.api.ODataConstants;
+import org.apache.olingo.client.api.domain.ODataJClientEdmPrimitiveType;
+import org.apache.olingo.client.api.domain.geospatial.Geospatial;
+import org.apache.olingo.client.api.utils.XMLUtils;
+import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+final class GeospatialJSONHandler {
+
+  /**
+   * Logger.
+   */
+  private static final Logger LOG = LoggerFactory.getLogger(GeospatialJSONHandler.class);
+
+  private GeospatialJSONHandler() {
+    // Empty private constructor for static utility classes
+  }
+
+  private static Element deserializePoint(final Document document, final Iterator<JsonNode> itor) {
+    final Element point = document.createElementNS(ODataConstants.NS_GML, ODataConstants.ELEM_POINT);
+
+    final Element ppos = document.createElementNS(ODataConstants.NS_GML, ODataConstants.ELEM_POS);
+    point.appendChild(ppos);
+    if (itor.hasNext()) {
+      ppos.appendChild(document.createTextNode(itor.next().asText() + " " + itor.next().asText()));
+    }
+
+    return point;
+  }
+
+  private static void appendPoses(final Element parent, final Document document, final Iterator<JsonNode> itor) {
+    while (itor.hasNext()) {
+      final Iterator<JsonNode> lineItor = itor.next().elements();
+      final Element pos = document.createElementNS(ODataConstants.NS_GML, ODataConstants.ELEM_POS);
+      parent.appendChild(pos);
+      pos.appendChild(document.createTextNode(lineItor.next().asText() + " " + lineItor.next().asText()));
+    }
+  }
+
+  private static Element deserializeLineString(final Document document, final Iterator<JsonNode> itor) {
+    final Element lineString = document.createElementNS(ODataConstants.NS_GML, ODataConstants.ELEM_LINESTRING);
+    if (!itor.hasNext()) {
+      lineString.appendChild(document.createElementNS(ODataConstants.NS_GML, ODataConstants.ELEM_POSLIST));
+    }
+
+    appendPoses(lineString, document, itor);
+
+    return lineString;
+  }
+
+  private static Element deserializePolygon(final Document document, final Iterator<JsonNode> itor) {
+    final Element polygon = document.createElementNS(ODataConstants.NS_GML, ODataConstants.ELEM_POLYGON);
+
+    if (itor.hasNext()) {
+      final Iterator<JsonNode> extItor = itor.next().elements();
+      final Element exterior = document.createElementNS(
+              ODataConstants.NS_GML, ODataConstants.ELEM_POLYGON_EXTERIOR);
+      polygon.appendChild(exterior);
+      final Element extLR = document.createElementNS(
+              ODataConstants.NS_GML, ODataConstants.ELEM_POLYGON_LINEARRING);
+      exterior.appendChild(extLR);
+
+      appendPoses(extLR, document, extItor);
+    }
+
+    if (itor.hasNext()) {
+      final Iterator<JsonNode> intItor = itor.next().elements();
+      final Element interior = document.createElementNS(
+              ODataConstants.NS_GML, ODataConstants.ELEM_POLYGON_INTERIOR);
+      polygon.appendChild(interior);
+      final Element intLR = document.createElementNS(
+              ODataConstants.NS_GML, ODataConstants.ELEM_POLYGON_LINEARRING);
+      interior.appendChild(intLR);
+
+      appendPoses(intLR, document, intItor);
+    }
+
+    return polygon;
+  }
+
+  public static void deserialize(final JsonNode node, final Element parent, final String type) {
+    final Iterator<JsonNode> cooItor = node.has(ODataConstants.JSON_COORDINATES)
+            ? node.get(ODataConstants.JSON_COORDINATES).elements()
+            : Collections.<JsonNode>emptyList().iterator();
+
+    Element root = null;
+    final ODataJClientEdmPrimitiveType edmSimpleType = ODataJClientEdmPrimitiveType.fromValue(type);
+    switch (edmSimpleType) {
+      case GeographyPoint:
+      case GeometryPoint:
+        root = deserializePoint(parent.getOwnerDocument(), cooItor);
+        break;
+
+      case GeographyMultiPoint:
+      case GeometryMultiPoint:
+        root = parent.getOwnerDocument().createElementNS(ODataConstants.NS_GML, ODataConstants.ELEM_MULTIPOINT);
+        if (cooItor.hasNext()) {
+          final Element pointMembers = parent.getOwnerDocument().createElementNS(
+                  ODataConstants.NS_GML, ODataConstants.ELEM_POINTMEMBERS);
+          root.appendChild(pointMembers);
+          while (cooItor.hasNext()) {
+            final Iterator<JsonNode> mpItor = cooItor.next().elements();
+            pointMembers.appendChild(deserializePoint(parent.getOwnerDocument(), mpItor));
+          }
+        }
+        break;
+
+      case GeographyLineString:
+      case GeometryLineString:
+        root = deserializeLineString(parent.getOwnerDocument(), cooItor);
+        break;
+
+      case GeographyMultiLineString:
+      case GeometryMultiLineString:
+        root = parent.getOwnerDocument().createElementNS(
+                ODataConstants.NS_GML, ODataConstants.ELEM_MULTILINESTRING);
+        if (cooItor.hasNext()) {
+          final Element lineStringMembers = parent.getOwnerDocument().createElementNS(
+                  ODataConstants.NS_GML, ODataConstants.ELEM_LINESTRINGMEMBERS);
+          root.appendChild(lineStringMembers);
+          while (cooItor.hasNext()) {
+            final Iterator<JsonNode> mlsItor = cooItor.next().elements();
+            lineStringMembers.appendChild(deserializeLineString(parent.getOwnerDocument(), mlsItor));
+          }
+        }
+        break;
+
+      case GeographyPolygon:
+      case GeometryPolygon:
+        root = deserializePolygon(parent.getOwnerDocument(), cooItor);
+        break;
+
+      case GeographyMultiPolygon:
+      case GeometryMultiPolygon:
+        root = parent.getOwnerDocument().createElementNS(
+                ODataConstants.NS_GML, ODataConstants.ELEM_MULTIPOLYGON);
+        if (cooItor.hasNext()) {
+          final Element surfaceMembers = parent.getOwnerDocument().createElementNS(
+                  ODataConstants.NS_GML, ODataConstants.ELEM_SURFACEMEMBERS);
+          root.appendChild(surfaceMembers);
+          while (cooItor.hasNext()) {
+            final Iterator<JsonNode> mpItor = cooItor.next().elements();
+            surfaceMembers.appendChild(deserializePolygon(parent.getOwnerDocument(), mpItor));
+          }
+        }
+        break;
+
+      case GeographyCollection:
+      case GeometryCollection:
+        root = parent.getOwnerDocument().createElementNS(
+                ODataConstants.NS_GML, ODataConstants.ELEM_GEOCOLLECTION);
+        if (node.has(ODataConstants.JSON_GEOMETRIES)) {
+          final Iterator<JsonNode> geoItor = node.get(ODataConstants.JSON_GEOMETRIES).elements();
+          if (geoItor.hasNext()) {
+            final Element geometryMembers = parent.getOwnerDocument().createElementNS(
+                    ODataConstants.NS_GML, ODataConstants.ELEM_GEOMEMBERS);
+            root.appendChild(geometryMembers);
+
+            while (geoItor.hasNext()) {
+              final JsonNode geo = geoItor.next();
+              final String collItemType = geo.get(ODataConstants.ATTR_TYPE).asText();
+              final String callAsType;
+              if (ODataJClientEdmPrimitiveType.GeographyCollection.name().equals(collItemType)
+                      || ODataJClientEdmPrimitiveType.GeometryCollection.name().equals(collItemType)) {
+
+                callAsType = collItemType;
+              } else {
+                callAsType =
+                        (edmSimpleType == ODataJClientEdmPrimitiveType.GeographyCollection ? "Geography" : "Geometry")
+                        + collItemType;
+              }
+
+              deserialize(geo, geometryMembers, ODataJClientEdmPrimitiveType.namespace() + "." + callAsType);
+            }
+          }
+        }
+        break;
+
+      default:
+    }
+
+    if (root != null) {
+      parent.appendChild(root);
+      if (node.has(ODataConstants.JSON_CRS)) {
+        root.setAttribute(ODataConstants.ATTR_SRSNAME,
+                ODataConstants.JSON_GIS_URLPREFIX
+                + node.get(ODataConstants.JSON_CRS).get(ODataConstants.PROPERTIES).get(ODataConstants.NAME).
+                asText().split(":")[1]);
+      }
+    }
+  }
+
+  private static void serializeCrs(final JsonGenerator jgen, final Node node) throws IOException {
+    if (node.getAttributes().getNamedItem(ODataConstants.ATTR_SRSNAME) != null) {
+      final String srsName = node.getAttributes().getNamedItem(ODataConstants.ATTR_SRSNAME).getTextContent();
+      final int prefIdx = srsName.indexOf(ODataConstants.JSON_GIS_URLPREFIX);
+      final String crsValue = srsName.substring(prefIdx + ODataConstants.JSON_GIS_URLPREFIX.length());
+
+      jgen.writeObjectFieldStart(ODataConstants.JSON_CRS);
+      jgen.writeStringField(ODataConstants.ATTR_TYPE, ODataConstants.NAME);
+      jgen.writeObjectFieldStart(ODataConstants.PROPERTIES);
+      jgen.writeStringField(ODataConstants.NAME, "EPSG:" + crsValue);
+      jgen.writeEndObject();
+      jgen.writeEndObject();
+    }
+  }
+
+  private static void serializePoint(final JsonGenerator jgen, final Node node) throws IOException {
+    for (String coord : node.getTextContent().split(" ")) {
+      jgen.writeNumber(coord);
+    }
+  }
+
+  private static void serializeLineString(final JsonGenerator jgen, final Element node) throws IOException {
+    for (Element element : XMLUtils.getChildElements(node, ODataConstants.ELEM_POS)) {
+      jgen.writeStartArray();
+      serializePoint(jgen, element);
+      jgen.writeEndArray();
+    }
+  }
+
+  private static void serializePolygon(final JsonGenerator jgen, final Element node) throws IOException {
+    for (Element exterior : XMLUtils.getChildElements(node, ODataConstants.ELEM_POLYGON_EXTERIOR)) {
+      jgen.writeStartArray();
+      serializeLineString(jgen,
+              XMLUtils.getChildElements(exterior, ODataConstants.ELEM_POLYGON_LINEARRING).get(0));
+      jgen.writeEndArray();
+
+    }
+    for (Element interior : XMLUtils.getChildElements(node, ODataConstants.ELEM_POLYGON_INTERIOR)) {
+      jgen.writeStartArray();
+      serializeLineString(jgen,
+              XMLUtils.getChildElements(interior, ODataConstants.ELEM_POLYGON_LINEARRING).get(0));
+      jgen.writeEndArray();
+
+    }
+  }
+
+  public static void serialize(final ODataClient client,
+          final JsonGenerator jgen, final Element node, final String type) throws IOException {
+
+    final ODataJClientEdmPrimitiveType edmSimpleType = ODataJClientEdmPrimitiveType.fromValue(type);
+
+    if (edmSimpleType.equals(ODataJClientEdmPrimitiveType.GeographyCollection)
+            || edmSimpleType.equals(ODataJClientEdmPrimitiveType.GeometryCollection)) {
+
+      jgen.writeStringField(ODataConstants.ATTR_TYPE, ODataJClientEdmPrimitiveType.GeometryCollection.name());
+    } else {
+      final int yIdx = edmSimpleType.name().indexOf('y');
+      final String itemType = edmSimpleType.name().substring(yIdx + 1);
+      jgen.writeStringField(ODataConstants.ATTR_TYPE, itemType);
+    }
+
+    Element root = null;
+    switch (edmSimpleType) {
+      case GeographyPoint:
+      case GeometryPoint:
+        root = XMLUtils.getChildElements(node, ODataConstants.ELEM_POINT).get(0);
+
+        jgen.writeArrayFieldStart(ODataConstants.JSON_COORDINATES);
+        serializePoint(jgen, XMLUtils.getChildElements(root, ODataConstants.ELEM_POS).get(0));
+        jgen.writeEndArray();
+        break;
+
+      case GeographyMultiPoint:
+      case GeometryMultiPoint:
+        root = XMLUtils.getChildElements(node, ODataConstants.ELEM_MULTIPOINT).get(0);
+
+        jgen.writeArrayFieldStart(ODataConstants.JSON_COORDINATES);
+
+        final List<Element> pMembs = XMLUtils.getChildElements(root, ODataConstants.ELEM_POINTMEMBERS);
+        if (pMembs != null && !pMembs.isEmpty()) {
+          for (Element point : XMLUtils.getChildElements(pMembs.get(0), ODataConstants.ELEM_POINT)) {
+            jgen.writeStartArray();
+            serializePoint(jgen, XMLUtils.getChildElements(point, ODataConstants.ELEM_POS).get(0));
+            jgen.writeEndArray();
+          }
+        }
+
+        jgen.writeEndArray();
+        break;
+
+      case GeographyLineString:
+      case GeometryLineString:
+        root = XMLUtils.getChildElements(node, ODataConstants.ELEM_LINESTRING).get(0);
+
+        jgen.writeArrayFieldStart(ODataConstants.JSON_COORDINATES);
+        serializeLineString(jgen, root);
+        jgen.writeEndArray();
+        break;
+
+      case GeographyMultiLineString:
+      case GeometryMultiLineString:
+        root = XMLUtils.getChildElements(node, ODataConstants.ELEM_MULTILINESTRING).get(0);
+
+        jgen.writeArrayFieldStart(ODataConstants.JSON_COORDINATES);
+
+        final List<Element> lMembs = XMLUtils.getChildElements(root, ODataConstants.ELEM_LINESTRINGMEMBERS);
+        if (lMembs != null && !lMembs.isEmpty()) {
+          for (Element lineStr : XMLUtils.getChildElements(lMembs.get(0), ODataConstants.ELEM_LINESTRING)) {
+            jgen.writeStartArray();
+            serializeLineString(jgen, lineStr);
+            jgen.writeEndArray();
+          }
+        }
+
+        jgen.writeEndArray();
+        break;
+
+      case GeographyPolygon:
+      case GeometryPolygon:
+        root = XMLUtils.getChildElements(node, ODataConstants.ELEM_POLYGON).get(0);
+
+        jgen.writeArrayFieldStart(ODataConstants.JSON_COORDINATES);
+        serializePolygon(jgen, root);
+        jgen.writeEndArray();
+        break;
+
+      case GeographyMultiPolygon:
+      case GeometryMultiPolygon:
+        root = XMLUtils.getChildElements(node, ODataConstants.ELEM_MULTIPOLYGON).get(0);
+
+        jgen.writeArrayFieldStart(ODataConstants.JSON_COORDINATES);
+
+        final List<Element> mpMembs = XMLUtils.getChildElements(root, ODataConstants.ELEM_SURFACEMEMBERS);
+        if (mpMembs != null & !mpMembs.isEmpty()) {
+          for (Element pol : XMLUtils.getChildElements(mpMembs.get(0), ODataConstants.ELEM_POLYGON)) {
+            jgen.writeStartArray();
+            serializePolygon(jgen, pol);
+            jgen.writeEndArray();
+          }
+        }
+
+        jgen.writeEndArray();
+        break;
+
+      case GeographyCollection:
+      case GeometryCollection:
+        root = XMLUtils.getChildElements(node, ODataConstants.ELEM_GEOCOLLECTION).get(0);
+
+        final List<Element> cMembs = XMLUtils.getChildElements(root, ODataConstants.ELEM_GEOMEMBERS);
+        if (cMembs != null && !cMembs.isEmpty()) {
+          jgen.writeArrayFieldStart(ODataConstants.JSON_GEOMETRIES);
+
+          for (Node geom : XMLUtils.getChildNodes(cMembs.get(0), Node.ELEMENT_NODE)) {
+            try {
+              final DocumentBuilder builder = XMLUtils.DOC_BUILDER_FACTORY.newDocumentBuilder();
+              final Document doc = builder.newDocument();
+
+              final Element fakeParent = doc.createElementNS(
+                      client.getServiceVersion().getNamespaceMap().
+                      get(ODataServiceVersion.NS_DATASERVICES),
+                      ODataConstants.PREFIX_DATASERVICES + "fake");
+              fakeParent.appendChild(doc.importNode(geom, true));
+
+              final ODataJClientEdmPrimitiveType callAsType = XMLUtils.simpleTypeForNode(
+                      edmSimpleType == ODataJClientEdmPrimitiveType.GeographyCollection
+                      ? Geospatial.Dimension.GEOGRAPHY : Geospatial.Dimension.GEOMETRY,
+                      geom);
+
+              jgen.writeStartObject();
+              serialize(client, jgen, fakeParent, callAsType.toString());
+              jgen.writeEndObject();
+            } catch (Exception e) {
+              LOG.warn("While serializing {}", XMLUtils.getSimpleName(geom), e);
+            }
+          }
+
+          jgen.writeEndArray();
+        }
+        break;
+
+      default:
+    }
+
+    if (root != null) {
+      serializeCrs(jgen, root);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/78c3eaa4/lib/client-core/src/main/java/org/apache/olingo/client/core/data/JSONDOMTreeUtils.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/data/JSONDOMTreeUtils.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/JSONDOMTreeUtils.java
new file mode 100644
index 0000000..bfe36ff
--- /dev/null
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/JSONDOMTreeUtils.java
@@ -0,0 +1,260 @@
+/*
+ * 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.client.core.data;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JsonNode;
+import java.io.IOException;
+import java.util.Iterator;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.olingo.client.api.ODataClient;
+import org.apache.olingo.client.api.ODataConstants;
+import org.apache.olingo.client.api.domain.ODataJClientEdmPrimitiveType;
+import org.apache.olingo.client.api.domain.ODataPrimitiveValue;
+import org.apache.olingo.client.api.utils.XMLUtils;
+import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+/**
+ * DOM tree utilities class for JSON.
+ */
+final class JSONDOMTreeUtils {
+
+  private JSONDOMTreeUtils() {
+    // Empty private constructor for static utility classes
+  }
+
+  /**
+   * Recursively builds DOM content out of JSON subtree rooted at given node.
+   *
+   * @param client OData client.
+   * @param document root of the DOM document being built
+   * @param parent parent of the nodes being generated during this step
+   * @param node JSON node to be used as source for DOM elements
+   */
+  public static void buildSubtree(final ODataClient client, final Element parent, final JsonNode node) {
+    final Iterator<String> fieldNameItor = node.fieldNames();
+    final Iterator<JsonNode> nodeItor = node.elements();
+    while (nodeItor.hasNext()) {
+      final JsonNode child = nodeItor.next();
+      final String name = fieldNameItor.hasNext() ? fieldNameItor.next() : "";
+
+      // no name? array item
+      if (name.isEmpty()) {
+        final Element element = parent.getOwnerDocument().createElementNS(
+                client.getServiceVersion().getNamespaceMap().get(ODataServiceVersion.NS_DATASERVICES),
+                ODataConstants.PREFIX_DATASERVICES + ODataConstants.ELEM_ELEMENT);
+        parent.appendChild(element);
+
+        if (child.isValueNode()) {
+          if (child.isNull()) {
+            element.setAttributeNS(
+                    client.getServiceVersion().getNamespaceMap().get(ODataServiceVersion.NS_METADATA),
+                    ODataConstants.ATTR_NULL, Boolean.toString(true));
+          } else {
+            element.appendChild(parent.getOwnerDocument().createTextNode(child.asText()));
+          }
+        }
+
+        if (child.isContainerNode()) {
+          buildSubtree(client, element, child);
+        }
+      } else if (!name.contains("@") && !ODataConstants.JSON_TYPE.equals(name)) {
+        final Element property = parent.getOwnerDocument().createElementNS(
+                client.getServiceVersion().getNamespaceMap().get(ODataServiceVersion.NS_DATASERVICES),
+                ODataConstants.PREFIX_DATASERVICES + name);
+        parent.appendChild(property);
+
+        boolean typeSet = false;
+        if (node.hasNonNull(name + "@" + ODataConstants.JSON_TYPE)) {
+          property.setAttributeNS(
+                  client.getServiceVersion().getNamespaceMap().get(ODataServiceVersion.NS_METADATA),
+                  ODataConstants.ATTR_M_TYPE,
+                  node.get(name + "@" + ODataConstants.JSON_TYPE).textValue());
+          typeSet = true;
+        }
+
+        if (child.isNull()) {
+          property.setAttributeNS(client.getServiceVersion().getNamespaceMap().
+                  get(ODataServiceVersion.NS_METADATA),
+                  ODataConstants.ATTR_NULL, Boolean.toString(true));
+        } else if (child.isValueNode()) {
+          if (!typeSet) {
+            if (child.isInt()) {
+              property.setAttributeNS(
+                      client.getServiceVersion().getNamespaceMap().get(ODataServiceVersion.NS_METADATA),
+                      ODataConstants.ATTR_M_TYPE, ODataJClientEdmPrimitiveType.Int32.toString());
+            }
+            if (child.isLong()) {
+              property.setAttributeNS(
+                      client.getServiceVersion().getNamespaceMap().get(ODataServiceVersion.NS_METADATA),
+                      ODataConstants.ATTR_M_TYPE, ODataJClientEdmPrimitiveType.Int64.toString());
+            }
+            if (child.isBigDecimal()) {
+              property.setAttributeNS(
+                      client.getServiceVersion().getNamespaceMap().get(ODataServiceVersion.NS_METADATA),
+                      ODataConstants.ATTR_M_TYPE, ODataJClientEdmPrimitiveType.Decimal.toString());
+            }
+            if (child.isDouble()) {
+              property.setAttributeNS(
+                      client.getServiceVersion().getNamespaceMap().get(ODataServiceVersion.NS_METADATA),
+                      ODataConstants.ATTR_M_TYPE, ODataJClientEdmPrimitiveType.Double.toString());
+            }
+            if (child.isBoolean()) {
+              property.setAttributeNS(
+                      client.getServiceVersion().getNamespaceMap().get(ODataServiceVersion.NS_METADATA),
+                      ODataConstants.ATTR_M_TYPE, ODataJClientEdmPrimitiveType.Boolean.toString());
+            }
+            if (child.isTextual()) {
+              property.setAttributeNS(
+                      client.getServiceVersion().getNamespaceMap().get(ODataServiceVersion.NS_METADATA),
+                      ODataConstants.ATTR_M_TYPE, ODataJClientEdmPrimitiveType.String.toString());
+            }
+          }
+
+          property.appendChild(parent.getOwnerDocument().createTextNode(child.asText()));
+        } else if (child.isContainerNode()) {
+          if (!typeSet && child.hasNonNull(ODataConstants.JSON_TYPE)) {
+            property.
+                    setAttributeNS(client.getServiceVersion().getNamespaceMap().
+                            get(ODataServiceVersion.NS_METADATA),
+                            ODataConstants.ATTR_M_TYPE,
+                            child.get(ODataConstants.JSON_TYPE).textValue());
+          }
+
+          final String type = property.getAttribute(ODataConstants.ATTR_M_TYPE);
+          if (StringUtils.isNotBlank(type) && ODataJClientEdmPrimitiveType.isGeospatial(type)) {
+            if (ODataJClientEdmPrimitiveType.Geography.toString().equals(type)
+                    || ODataJClientEdmPrimitiveType.Geometry.toString().equals(type)) {
+
+              final String geoType = child.get(ODataConstants.ATTR_TYPE).textValue();
+              property.setAttributeNS(client.getServiceVersion().getNamespaceMap().get(
+                      ODataServiceVersion.NS_METADATA), ODataConstants.ATTR_M_TYPE,
+                      geoType.startsWith("Geo")
+                      ? ODataJClientEdmPrimitiveType.namespace() + "." + geoType
+                      : type + geoType);
+            }
+
+            if (child.has(ODataConstants.JSON_COORDINATES) || child.has(ODataConstants.JSON_GEOMETRIES)) {
+              GeospatialJSONHandler.deserialize(
+                      child, property, property.getAttribute(ODataConstants.ATTR_M_TYPE));
+            }
+          } else {
+            buildSubtree(client, property, child);
+          }
+        }
+      }
+    }
+  }
+
+  /**
+   * Serializes DOM content as JSON.
+   *
+   * @param client OData client.
+   * @param jgen JSON generator.
+   * @param content content.
+   * @throws IOException in case of write error.
+   */
+  public static void writeSubtree(final ODataClient client, final JsonGenerator jgen, final Node content)
+          throws IOException {
+
+    writeSubtree(client, jgen, content, false);
+  }
+
+  /**
+   * Serializes DOM content as JSON.
+   *
+   * @param client OData client.
+   * @param jgen JSON generator.
+   * @param content content.
+   * @param propType whether to output type information in the way needed for property values or not.
+   * @throws IOException in case of write error.
+   */
+  public static void writeSubtree(
+          final ODataClient client, final JsonGenerator jgen, final Node content, final boolean propType)
+          throws IOException {
+
+    for (Node child : XMLUtils.getChildNodes(content, Node.ELEMENT_NODE)) {
+      final String childName = XMLUtils.getSimpleName(child);
+
+      final Node typeAttr = child.getAttributes().getNamedItem(ODataConstants.ATTR_M_TYPE);
+      if (typeAttr != null && ODataJClientEdmPrimitiveType.isGeospatial(typeAttr.getTextContent())) {
+        jgen.writeStringField(propType ? ODataConstants.JSON_TYPE : childName + "@" + ODataConstants.JSON_TYPE,
+                typeAttr.getTextContent());
+
+        jgen.writeObjectFieldStart(childName);
+        GeospatialJSONHandler.serialize(client, jgen, (Element) child, typeAttr.getTextContent());
+        jgen.writeEndObject();
+      } else if (XMLUtils.hasOnlyTextChildNodes(child)) {
+        if (child.hasChildNodes()) {
+          final String out;
+          if (typeAttr == null) {
+            out = child.getChildNodes().item(0).getNodeValue();
+          } else {
+            final ODataJClientEdmPrimitiveType type = ODataJClientEdmPrimitiveType.fromValue(typeAttr.getTextContent());
+            final ODataPrimitiveValue value = client.getPrimitiveValueBuilder().setType(type).
+                    setText(child.getChildNodes().item(0).getNodeValue()).build();
+            out = value.toString();
+
+            jgen.writeStringField(childName + "@" + ODataConstants.JSON_TYPE, type.toString());
+          }
+          jgen.writeStringField(childName, out);
+        } else {
+          if (child.getAttributes().getNamedItem(ODataConstants.ATTR_NULL) == null) {
+            if (typeAttr != null && ODataJClientEdmPrimitiveType.String.toString().equals(typeAttr.getTextContent())) {
+              jgen.writeStringField(childName + "@" + ODataConstants.JSON_TYPE, typeAttr.getTextContent());
+              jgen.writeStringField(childName, StringUtils.EMPTY);
+            } else {
+              jgen.writeArrayFieldStart(childName);
+              jgen.writeEndArray();
+            }
+          } else {
+            jgen.writeNullField(childName);
+          }
+        }
+      } else {
+        if (XMLUtils.hasElementsChildNode(child)) {
+          jgen.writeArrayFieldStart(childName);
+
+          for (Node nephew : XMLUtils.getChildNodes(child, Node.ELEMENT_NODE)) {
+            if (XMLUtils.hasOnlyTextChildNodes(nephew)) {
+              jgen.writeString(nephew.getChildNodes().item(0).getNodeValue());
+            } else {
+              jgen.writeStartObject();
+              writeSubtree(client, jgen, nephew);
+              jgen.writeEndObject();
+            }
+          }
+
+          jgen.writeEndArray();
+        } else {
+          jgen.writeObjectFieldStart(childName);
+          if (typeAttr != null) {
+            jgen.writeStringField(ODataConstants.JSON_TYPE, typeAttr.getTextContent());
+          }
+
+          writeSubtree(client, jgen, child);
+
+          jgen.writeEndObject();
+        }
+      }
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/78c3eaa4/lib/client-core/src/main/java/org/apache/olingo/client/core/data/JSONEntryDeserializer.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/data/JSONEntryDeserializer.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/JSONEntryDeserializer.java
new file mode 100644
index 0000000..0cfac43
--- /dev/null
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/JSONEntryDeserializer.java
@@ -0,0 +1,240 @@
+/*
+ * 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.client.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.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.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.ParserConfigurationException;
+import org.apache.olingo.client.api.ODataConstants;
+import org.apache.olingo.client.api.data.LinkType;
+import org.apache.olingo.client.api.utils.XMLUtils;
+import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+/**
+ * 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 ODataJacksonDeserializer<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) {
+        final JsonParser inlineParser = inline.traverse();
+        inlineParser.setCodec(codec);
+        link.setInlineEntry(inlineParser.readValuesAs(JSONEntryImpl.class).next());
+      }
+
+      if (inline instanceof ArrayNode) {
+        final JSONFeedImpl feed = new JSONFeedImpl();
+        final Iterator<JsonNode> entries = ((ArrayNode) inline).elements();
+        while (entries.hasNext()) {
+          final JsonParser inlineParser = entries.next().traverse();
+          inlineParser.setCodec(codec);
+          feed.getEntries().add(inlineParser.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(ODataConstants.JSON_VALUE) && tree.get(ODataConstants.JSON_VALUE).isArray()) {
+      throw new JsonParseException("Expected OData Entity, found EntitySet", parser.getCurrentLocation());
+    }
+
+    final boolean isMediaEntry =
+            tree.hasNonNull(ODataConstants.JSON_MEDIAREAD_LINK)
+            && tree.hasNonNull(ODataConstants.JSON_MEDIA_CONTENT_TYPE);
+
+    final JSONEntryImpl entry = new JSONEntryImpl();
+
+    if (tree.hasNonNull(ODataConstants.JSON_METADATA)) {
+      entry.setMetadata(URI.create(tree.get(ODataConstants.JSON_METADATA).textValue()));
+      tree.remove(ODataConstants.JSON_METADATA);
+    }
+
+    if (tree.hasNonNull(ODataConstants.JSON_MEDIA_ETAG)) {
+      entry.setMediaETag(tree.get(ODataConstants.JSON_MEDIA_ETAG).textValue());
+      tree.remove(ODataConstants.JSON_MEDIA_ETAG);
+    }
+
+    if (tree.hasNonNull(ODataConstants.JSON_ETAG)) {
+      entry.setETag(tree.get(ODataConstants.JSON_ETAG).textValue());
+      tree.remove(ODataConstants.JSON_ETAG);
+    }
+
+    if (tree.hasNonNull(ODataConstants.JSON_TYPE)) {
+      entry.setType(tree.get(ODataConstants.JSON_TYPE).textValue());
+      tree.remove(ODataConstants.JSON_TYPE);
+    }
+
+    if (tree.hasNonNull(ODataConstants.JSON_ID)) {
+      entry.setId(tree.get(ODataConstants.JSON_ID).textValue());
+      tree.remove(ODataConstants.JSON_ID);
+    }
+
+    if (tree.hasNonNull(ODataConstants.JSON_READ_LINK)) {
+      final LinkImpl link = new LinkImpl();
+      link.setRel(ODataConstants.SELF_LINK_REL);
+      link.setHref(tree.get(ODataConstants.JSON_READ_LINK).textValue());
+      entry.setSelfLink(link);
+
+      tree.remove(ODataConstants.JSON_READ_LINK);
+    }
+
+    if (tree.hasNonNull(ODataConstants.JSON_EDIT_LINK)) {
+      final LinkImpl link = new LinkImpl();
+      link.setRel(ODataConstants.EDIT_LINK_REL);
+      link.setHref(tree.get(ODataConstants.JSON_EDIT_LINK).textValue());
+      entry.setEditLink(link);
+
+      tree.remove(ODataConstants.JSON_EDIT_LINK);
+    }
+
+    if (tree.hasNonNull(ODataConstants.JSON_MEDIAREAD_LINK)) {
+      entry.setMediaContentSource(tree.get(ODataConstants.JSON_MEDIAREAD_LINK).textValue());
+      tree.remove(ODataConstants.JSON_MEDIAREAD_LINK);
+    }
+    if (tree.hasNonNull(ODataConstants.JSON_MEDIAEDIT_LINK)) {
+      /*final LinkImpl link = new LinkImpl();
+      link.setHref(tree.get(ODataConstants.JSON_MEDIAEDIT_LINK).textValue());
+      entry.getMediaEditLinks().add(link);*/
+
+      tree.remove(ODataConstants.JSON_MEDIAEDIT_LINK);
+    }
+    if (tree.hasNonNull(ODataConstants.JSON_MEDIA_CONTENT_TYPE)) {
+      entry.setMediaContentType(tree.get(ODataConstants.JSON_MEDIA_CONTENT_TYPE).textValue());
+      tree.remove(ODataConstants.JSON_MEDIA_CONTENT_TYPE);
+    }
+
+    final Set<String> toRemove = new HashSet<String>();
+    final Iterator<Map.Entry<String, JsonNode>> itor = tree.fields();
+    while (itor.hasNext()) {
+      final Map.Entry<String, JsonNode> field = itor.next();
+
+      if (field.getKey().endsWith(ODataConstants.JSON_NAVIGATION_LINK_SUFFIX)) {
+        final LinkImpl link = new LinkImpl();
+        link.setTitle(getTitle(field));
+        link.setRel(client.getServiceVersion().getNamespaceMap().get(ODataServiceVersion.NAVIGATION_LINK_REL)
+                + getTitle(field));
+        if (field.getValue().isValueNode()) {
+          link.setHref(field.getValue().textValue());
+          link.setType(LinkType.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(LinkType.ENTITY_SET_NAVIGATION.toString());
+         * } */
+        entry.getNavigationLinks().add(link);
+
+        toRemove.add(field.getKey());
+        toRemove.add(setInline(field.getKey(),
+                ODataConstants.JSON_NAVIGATION_LINK_SUFFIX, tree, parser.getCodec(), link));
+      } else if (field.getKey().endsWith(ODataConstants.JSON_ASSOCIATION_LINK_SUFFIX)) {
+        final LinkImpl link = new LinkImpl();
+        link.setTitle(getTitle(field));
+        link.setRel(client.getServiceVersion().getNamespaceMap().get(ODataServiceVersion.ASSOCIATION_LINK_REL)
+                + getTitle(field));
+        link.setHref(field.getValue().textValue());
+        link.setType(LinkType.ASSOCIATION.toString());
+        entry.getAssociationLinks().add(link);
+
+        toRemove.add(field.getKey());
+      } else if (field.getKey().endsWith(ODataConstants.JSON_MEDIAEDIT_LINK_SUFFIX)) {
+        final LinkImpl link = new LinkImpl();
+        link.setTitle(getTitle(field));
+        link.setRel(client.getServiceVersion().getNamespaceMap().get(ODataServiceVersion.MEDIA_EDIT_LINK_REL)
+                + getTitle(field));
+        link.setHref(field.getValue().textValue());
+        link.setType(LinkType.MEDIA_EDIT.toString());
+        entry.getMediaEditLinks().add(link);
+
+        toRemove.add(field.getKey());
+        toRemove.add(setInline(field.getKey(),
+                ODataConstants.JSON_MEDIAEDIT_LINK_SUFFIX, tree, parser.getCodec(), link));
+      } else if (field.getKey().charAt(0) == '#') {
+        final OperationImpl operation = new OperationImpl();
+        operation.setMetadataAnchor(field.getKey());
+
+        final ObjectNode opNode = (ObjectNode) tree.get(field.getKey());
+        operation.setTitle(opNode.get(ODataConstants.ATTR_TITLE).asText());
+        operation.setTarget(URI.create(opNode.get(ODataConstants.ATTR_TARGET).asText()));
+
+        entry.getOperations().add(operation);
+
+        toRemove.add(field.getKey());
+      }
+    }
+    tree.remove(toRemove);
+
+    try {
+      final DocumentBuilder builder = XMLUtils.DOC_BUILDER_FACTORY.newDocumentBuilder();
+      final Document document = builder.newDocument();
+
+      final Element properties = document.createElementNS(
+              client.getServiceVersion().getNamespaceMap().get(ODataServiceVersion.NS_METADATA),
+              ODataConstants.ELEM_PROPERTIES);
+
+      JSONDOMTreeUtils.buildSubtree(client, properties, tree);
+
+      if (isMediaEntry) {
+        entry.setMediaEntryProperties(properties);
+      } else {
+        entry.setContent(properties);
+      }
+    } catch (ParserConfigurationException e) {
+      throw new JsonParseException("Cannot build entry content", parser.getCurrentLocation(), e);
+    }
+
+    return entry;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/78c3eaa4/lib/client-core/src/main/java/org/apache/olingo/client/core/data/JSONEntryImpl.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/data/JSONEntryImpl.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/JSONEntryImpl.java
new file mode 100644
index 0000000..3b7aeda
--- /dev/null
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/JSONEntryImpl.java
@@ -0,0 +1,89 @@
+/*
+ * 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.client.core.data;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import java.net.URI;
+import org.apache.olingo.client.api.uri.SegmentType;
+
+/**
+ * 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;
+
+  @JsonIgnore
+  @Override
+  public URI getBaseURI() {
+    URI baseURI = null;
+    if (metadata != null) {
+      final String metadataURI = getMetadata().toASCIIString();
+      baseURI = URI.create(metadataURI.substring(0, metadataURI.indexOf(SegmentType.METADATA.getValue())));
+    }
+
+    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/78c3eaa4/lib/client-core/src/main/java/org/apache/olingo/client/core/data/JSONEntrySerializer.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/data/JSONEntrySerializer.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/JSONEntrySerializer.java
new file mode 100644
index 0000000..518ff55
--- /dev/null
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/JSONEntrySerializer.java
@@ -0,0 +1,128 @@
+/*
+ * 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.client.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.client.api.ODataConstants;
+import org.apache.olingo.client.api.data.Link;
+import org.apache.olingo.client.api.data.LinkType;
+
+/**
+ * Writes out JSON string from an entry.
+ */
+public class JSONEntrySerializer extends ODataJacksonSerializer<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(ODataConstants.JSON_METADATA, entry.getMetadata().toASCIIString());
+    }
+    if (StringUtils.isNotBlank(entry.getType())) {
+      jgen.writeStringField(ODataConstants.JSON_TYPE, entry.getType());
+    }
+    if (entry.getId() != null) {
+      jgen.writeStringField(ODataConstants.JSON_ID, entry.getId());
+    }
+
+    if (entry.getSelfLink() != null) {
+      jgen.writeStringField(ODataConstants.JSON_READ_LINK, entry.getSelfLink().getHref());
+    }
+
+    if (entry.getEditLink() != null) {
+      jgen.writeStringField(ODataConstants.JSON_EDIT_LINK, entry.getEditLink().getHref());
+    }
+
+    if (entry.getMediaContentSource() != null) {
+      jgen.writeStringField(ODataConstants.JSON_MEDIAREAD_LINK, entry.getMediaContentSource());
+    }
+    if (entry.getMediaContentType() != null) {
+      jgen.writeStringField(ODataConstants.JSON_MEDIA_CONTENT_TYPE, entry.getMediaContentType());
+    }
+
+    final Map<String, List<String>> entitySetLinks = new HashMap<String, List<String>>();
+
+    for (Link link : entry.getNavigationLinks()) {
+      if (link.getInlineEntry() != null) {
+        jgen.writeObjectField(link.getTitle(), link.getInlineEntry());
+      } else if (link.getInlineFeed() != null) {
+        jgen.writeObjectField(link.getTitle(), link.getInlineFeed());
+      } else {
+        LinkType type = null;
+        try {
+          type = LinkType.fromString(client, link.getRel(), link.getType());
+        } catch (IllegalArgumentException e) {
+          // ignore   
+        }
+
+        if (type == LinkType.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 {
+          jgen.writeStringField(link.getTitle() + ODataConstants.JSON_BIND_LINK_SUFFIX, link.getHref());
+        }
+      }
+    }
+    for (Map.Entry<String, List<String>> entitySetLink : entitySetLinks.entrySet()) {
+      jgen.writeArrayFieldStart(entitySetLink.getKey() + ODataConstants.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(ODataConstants.JSON_MEDIAEDIT_LINK, link.getHref());
+      }
+
+      if (link.getInlineEntry() != null) {
+        jgen.writeObjectField(link.getTitle(), link.getInlineEntry());
+      }
+      if (link.getInlineFeed() != null) {
+        jgen.writeObjectField(link.getTitle(), link.getInlineFeed());
+      }
+    }
+
+    if (entry.getMediaEntryProperties() == null) {
+      JSONDOMTreeUtils.writeSubtree(client, jgen, entry.getContent());
+    } else {
+      JSONDOMTreeUtils.writeSubtree(client, jgen, entry.getMediaEntryProperties());
+    }
+
+    jgen.writeEndObject();
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/78c3eaa4/lib/client-core/src/main/java/org/apache/olingo/client/core/data/JSONErrorBundle.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/data/JSONErrorBundle.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/JSONErrorBundle.java
new file mode 100644
index 0000000..a73fb05
--- /dev/null
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/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.client.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;
+  }
+}