http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/20351253/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonDeltaDeserializer.java ---------------------------------------------------------------------- diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonDeltaDeserializer.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonDeltaDeserializer.java new file mode 100644 index 0000000..c9b962a --- /dev/null +++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonDeltaDeserializer.java @@ -0,0 +1,100 @@ +/* + * 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.serialization; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; + +import org.apache.commons.lang3.StringUtils; +import org.apache.olingo.client.api.serialization.ODataDeserializerException; +import org.apache.olingo.commons.api.Constants; +import org.apache.olingo.commons.api.data.ContextURL; +import org.apache.olingo.commons.api.data.DeletedEntity; +import org.apache.olingo.commons.api.data.Delta; +import org.apache.olingo.commons.api.data.DeltaLink; +import org.apache.olingo.commons.api.data.ResWrap; + +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; + +public class JsonDeltaDeserializer extends JsonDeserializer { + + public JsonDeltaDeserializer(final boolean serverMode) { + super(serverMode); + } + + protected ResWrap<Delta> doDeserialize(final JsonParser parser) throws IOException { + + final ObjectNode tree = parser.getCodec().readTree(parser); + + final Delta delta = new Delta(); + + final URI contextURL = tree.hasNonNull(Constants.JSON_CONTEXT) ? + URI.create(tree.get(Constants.JSON_CONTEXT).textValue()) : null; + if (contextURL != null) { + delta.setBaseURI(URI.create(StringUtils.substringBefore(contextURL.toASCIIString(), Constants.METADATA))); + } + + if (tree.hasNonNull(Constants.JSON_COUNT)) { + delta.setCount(tree.get(Constants.JSON_COUNT).asInt()); + } + if (tree.hasNonNull(Constants.JSON_NEXT_LINK)) { + delta.setNext(URI.create(tree.get(Constants.JSON_NEXT_LINK).textValue())); + } + if (tree.hasNonNull(Constants.JSON_DELTA_LINK)) { + delta.setDeltaLink(URI.create(tree.get(Constants.JSON_DELTA_LINK).textValue())); + } + + if (tree.hasNonNull(Constants.VALUE)) { + JsonEntityDeserializer entityDeserializer = new JsonEntityDeserializer(serverMode); + for (JsonNode jsonNode : tree.get(Constants.VALUE)) { + final ObjectNode item = (ObjectNode) jsonNode; + final ContextURL itemContextURL = item.hasNonNull(Constants.JSON_CONTEXT) ? + ContextURLParser.parse(URI.create(item.get(Constants.JSON_CONTEXT).textValue())) : null; + item.remove(Constants.JSON_CONTEXT); + + if (itemContextURL == null || itemContextURL.isEntity()) { + delta.getEntities().add( + entityDeserializer.doDeserialize(item.traverse(parser.getCodec())).getPayload()); + } else if (itemContextURL.isDeltaDeletedEntity()) { + delta.getDeletedEntities().add(parser.getCodec().treeToValue(item, DeletedEntity.class)); + } else if (itemContextURL.isDeltaLink()) { + delta.getAddedLinks().add(parser.getCodec().treeToValue(item, DeltaLink.class)); + } else if (itemContextURL.isDeltaDeletedLink()) { + delta.getDeletedLinks().add(parser.getCodec().treeToValue(item, DeltaLink.class)); + } + } + } + + return new ResWrap<Delta>(contextURL, null, delta); + } + + public ResWrap<Delta> toDelta(final InputStream input) throws ODataDeserializerException { + try { + JsonParser parser = new JsonFactory(new ObjectMapper()).createParser(input); + return doDeserialize(parser); + } catch (final IOException e) { + throw new ODataDeserializerException(e); + } + } +}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/20351253/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonDeserializer.java ---------------------------------------------------------------------- diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonDeserializer.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonDeserializer.java new file mode 100644 index 0000000..ab70931 --- /dev/null +++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonDeserializer.java @@ -0,0 +1,421 @@ +/* + * 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.serialization; + +import java.io.IOException; +import java.io.InputStream; +import java.util.AbstractMap.SimpleEntry; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.commons.lang3.StringUtils; +import org.apache.olingo.client.api.serialization.ODataDeserializer; +import org.apache.olingo.client.api.serialization.ODataDeserializerException; +import org.apache.olingo.commons.api.Constants; +import org.apache.olingo.commons.api.ODataError; +import org.apache.olingo.commons.api.ODataPropertyType; +import org.apache.olingo.commons.api.data.Annotatable; +import org.apache.olingo.commons.api.data.Annotation; +import org.apache.olingo.commons.api.data.ComplexValue; +import org.apache.olingo.commons.api.data.Entity; +import org.apache.olingo.commons.api.data.EntityCollection; +import org.apache.olingo.commons.api.data.Link; +import org.apache.olingo.commons.api.data.Linked; +import org.apache.olingo.commons.api.data.Property; +import org.apache.olingo.commons.api.data.ResWrap; +import org.apache.olingo.commons.api.data.Valuable; +import org.apache.olingo.commons.api.data.ValueType; +import org.apache.olingo.commons.api.edm.EdmPrimitiveType; +import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException; +import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind; +import org.apache.olingo.commons.api.edm.geo.Geospatial; +import org.apache.olingo.commons.core.edm.EdmTypeInfo; + +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.ObjectCodec; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; + +public class JsonDeserializer implements ODataDeserializer { + + protected final Pattern CUSTOM_ANNOTATION = Pattern.compile("(.+)@(.+)\\.(.+)"); + + protected final boolean serverMode; + + private JsonGeoValueDeserializer geoDeserializer; + + private JsonParser parser; + + public JsonDeserializer(final boolean serverMode) { + this.serverMode = serverMode; + } + + private JsonGeoValueDeserializer getGeoDeserializer() { + if (geoDeserializer == null) { + geoDeserializer = new JsonGeoValueDeserializer(); + } + return geoDeserializer; + } + + protected String getJSONAnnotation(final String string) { + return StringUtils.prependIfMissing(string, "@"); + } + + protected String getTitle(final Map.Entry<String, JsonNode> entry) { + return entry.getKey().substring(0, entry.getKey().indexOf('@')); + } + + protected String setInline(final String name, final String suffix, final JsonNode tree, + final ObjectCodec codec, final Link link) throws IOException { + + final String entityNamePrefix = name.substring(0, name.indexOf(suffix)); + if (tree.has(entityNamePrefix)) { + final JsonNode inline = tree.path(entityNamePrefix); + JsonEntityDeserializer entityDeserializer = new JsonEntityDeserializer(serverMode); + + if (inline instanceof ObjectNode) { + link.setType(Constants.ENTITY_NAVIGATION_LINK_TYPE); + link.setInlineEntity(entityDeserializer.doDeserialize(inline.traverse(codec)).getPayload()); + + } else if (inline instanceof ArrayNode) { + link.setType(Constants.ENTITY_SET_NAVIGATION_LINK_TYPE); + + final EntityCollection entitySet = new EntityCollection(); + for (final Iterator<JsonNode> entries = inline.elements(); entries.hasNext();) { + entitySet.getEntities().add(entityDeserializer.doDeserialize(entries.next().traverse(codec)).getPayload()); + } + + link.setInlineEntitySet(entitySet); + } + } + return entityNamePrefix; + } + + protected void links(final Map.Entry<String, JsonNode> field, final Linked linked, final Set<String> toRemove, + final JsonNode tree, final ObjectCodec codec) throws IOException { + if (serverMode) { + serverLinks(field, linked, toRemove, tree, codec); + } else { + clientLinks(field, linked, toRemove, tree, codec); + } + } + + private void clientLinks(final Map.Entry<String, JsonNode> field, final Linked linked, final Set<String> toRemove, + final JsonNode tree, final ObjectCodec codec) throws IOException { + + if (field.getKey().endsWith(Constants.JSON_NAVIGATION_LINK)) { + final Link link = new Link(); + link.setTitle(getTitle(field)); + link.setRel(Constants.NS_NAVIGATION_LINK_REL + getTitle(field)); + + if (field.getValue().isValueNode()) { + link.setHref(field.getValue().textValue()); + link.setType(Constants.ENTITY_NAVIGATION_LINK_TYPE); + } + + linked.getNavigationLinks().add(link); + + toRemove.add(field.getKey()); + toRemove.add(setInline(field.getKey(), Constants.JSON_NAVIGATION_LINK, tree, codec, link)); + } else if (field.getKey().endsWith(Constants.JSON_ASSOCIATION_LINK)) { + final Link link = new Link(); + link.setTitle(getTitle(field)); + link.setRel(Constants.NS_ASSOCIATION_LINK_REL + getTitle(field)); + link.setHref(field.getValue().textValue()); + link.setType(Constants.ASSOCIATION_LINK_TYPE); + linked.getAssociationLinks().add(link); + + toRemove.add(field.getKey()); + } + } + + private void serverLinks(final Map.Entry<String, JsonNode> field, final Linked linked, final Set<String> toRemove, + final JsonNode tree, final ObjectCodec codec) throws IOException { + + if (field.getKey().endsWith(Constants.JSON_BIND_LINK_SUFFIX) + || field.getKey().endsWith(Constants.JSON_NAVIGATION_LINK)) { + + if (field.getValue().isValueNode()) { + final String suffix = field.getKey().replaceAll("^.*@", "@"); + + final Link link = new Link(); + link.setTitle(getTitle(field)); + link.setRel(Constants.NS_NAVIGATION_LINK_REL + getTitle(field)); + link.setHref(field.getValue().textValue()); + link.setType(Constants.ENTITY_NAVIGATION_LINK_TYPE); + linked.getNavigationLinks().add(link); + + toRemove.add(setInline(field.getKey(), suffix, tree, codec, link)); + } else if (field.getValue().isArray()) { + for (final Iterator<JsonNode> itor = field.getValue().elements(); itor.hasNext();) { + final JsonNode node = itor.next(); + + final Link link = new Link(); + link.setTitle(getTitle(field)); + link.setRel(Constants.NS_NAVIGATION_LINK_REL + getTitle(field)); + link.setHref(node.asText()); + link.setType(Constants.ENTITY_SET_NAVIGATION_LINK_TYPE); + linked.getNavigationLinks().add(link); + toRemove.add(setInline(field.getKey(), Constants.JSON_BIND_LINK_SUFFIX, tree, codec, link)); + } + } + toRemove.add(field.getKey()); + } + } + + private Map.Entry<ODataPropertyType, EdmTypeInfo> guessPropertyType(final JsonNode node) { + ODataPropertyType type; + String typeExpression = null; + + if (node.isValueNode() || node.isNull()) { + type = ODataPropertyType.PRIMITIVE; + typeExpression = guessPrimitiveTypeKind(node).getFullQualifiedName().toString(); + } else if (node.isArray()) { + type = ODataPropertyType.COLLECTION; + if (node.has(0) && node.get(0).isValueNode()) { + typeExpression = "Collection(" + guessPrimitiveTypeKind(node.get(0)) + ')'; + } + } else if (node.isObject()) { + if (node.has(Constants.ATTR_TYPE)) { + type = ODataPropertyType.PRIMITIVE; + typeExpression = "Edm.Geography" + node.get(Constants.ATTR_TYPE).asText(); + } else { + type = ODataPropertyType.COMPLEX; + } + } else { + type = ODataPropertyType.EMPTY; + } + + final EdmTypeInfo typeInfo = typeExpression == null ? null : + new EdmTypeInfo.Builder().setTypeExpression(typeExpression).build(); + return new SimpleEntry<ODataPropertyType, EdmTypeInfo>(type, typeInfo); + } + + private EdmPrimitiveTypeKind guessPrimitiveTypeKind(final JsonNode node) { + return node.isShort() ? EdmPrimitiveTypeKind.Int16 : + node.isInt() ? EdmPrimitiveTypeKind.Int32 : + node.isLong() ? EdmPrimitiveTypeKind.Int64 : + node.isBoolean() ? EdmPrimitiveTypeKind.Boolean : + node.isFloat() ? EdmPrimitiveTypeKind.Single : + node.isDouble() ? EdmPrimitiveTypeKind.Double : + node.isBigDecimal() ? EdmPrimitiveTypeKind.Decimal : + EdmPrimitiveTypeKind.String; + } + + protected void populate(final Annotatable annotatable, final List<Property> properties, + final ObjectNode tree, final ObjectCodec codec) + throws IOException, EdmPrimitiveTypeException { + + String type = null; + Annotation annotation = null; + for (final Iterator<Map.Entry<String, JsonNode>> itor = tree.fields(); itor.hasNext();) { + final Map.Entry<String, JsonNode> field = itor.next(); + final Matcher customAnnotation = CUSTOM_ANNOTATION.matcher(field.getKey()); + + if (field.getKey().charAt(0) == '@') { + final Annotation entityAnnot = new Annotation(); + entityAnnot.setTerm(field.getKey().substring(1)); + + value(entityAnnot, field.getValue(), codec); + if (annotatable != null) { + annotatable.getAnnotations().add(entityAnnot); + } + } else if (type == null && field.getKey().endsWith(getJSONAnnotation(Constants.JSON_TYPE))) { + type = field.getValue().asText(); + } else if (annotation == null && customAnnotation.matches() && !"odata".equals(customAnnotation.group(2))) { + annotation = new Annotation(); + annotation.setTerm(customAnnotation.group(2) + "." + customAnnotation.group(3)); + value(annotation, field.getValue(), codec); + } else { + final Property property = new Property(); + property.setName(field.getKey()); + property.setType(type == null + ? null + : new EdmTypeInfo.Builder().setTypeExpression(type).build().internal()); + type = null; + + value(property, field.getValue(), codec); + properties.add(property); + + if (annotation != null) { + property.getAnnotations().add(annotation); + annotation = null; + } + } + } + } + + private Object fromPrimitive(final JsonNode node, final EdmTypeInfo typeInfo) throws EdmPrimitiveTypeException { + return node.isNull() ? null + : typeInfo == null ? node.asText() + : typeInfo.getPrimitiveTypeKind().isGeospatial() + ? getGeoDeserializer().deserialize(node, typeInfo) + : ((EdmPrimitiveType) typeInfo.getType()) + .valueOfString(node.asText(), true, null, + Constants.DEFAULT_PRECISION, Constants.DEFAULT_SCALE, true, + ((EdmPrimitiveType) typeInfo.getType()).getDefaultType()); + } + + private Object fromComplex(final ObjectNode node, final ObjectCodec codec) + throws IOException, EdmPrimitiveTypeException { + + final ComplexValue complexValue = new ComplexValue(); + final Set<String> toRemove = new HashSet<String>(); + for (final Iterator<Map.Entry<String, JsonNode>> itor = node.fields(); itor.hasNext();) { + final Map.Entry<String, JsonNode> field = itor.next(); + + links(field, complexValue, toRemove, node, codec); + } + node.remove(toRemove); + + populate(complexValue, complexValue.getValue(), node, codec); + return complexValue; + } + + private void fromCollection(final Valuable valuable, final Iterator<JsonNode> nodeItor, final EdmTypeInfo typeInfo, + final ObjectCodec codec) throws IOException, EdmPrimitiveTypeException { + + final List<Object> values = new ArrayList<Object>(); + ValueType valueType = ValueType.COLLECTION_PRIMITIVE; + + final EdmTypeInfo type = typeInfo == null ? null + : new EdmTypeInfo.Builder().setTypeExpression(typeInfo.getFullQualifiedName().toString()).build(); + + while (nodeItor.hasNext()) { + final JsonNode child = nodeItor.next(); + + if (child.isValueNode()) { + if (typeInfo == null || typeInfo.isPrimitiveType()) { + final Object value = fromPrimitive(child, type); + valueType = value instanceof Geospatial ? ValueType.COLLECTION_GEOSPATIAL : ValueType.COLLECTION_PRIMITIVE; + values.add(value); + } else { + valueType = ValueType.COLLECTION_ENUM; + values.add(child.asText()); + } + } else if (child.isContainerNode()) { + if (child.has(Constants.JSON_TYPE)) { + ((ObjectNode) child).remove(Constants.JSON_TYPE); + } + final Object value = fromComplex((ObjectNode) child, codec); + valueType = ValueType.COLLECTION_COMPLEX; + values.add(value); + } + } + valuable.setValue(valueType, values); + } + + protected void value(final Valuable valuable, final JsonNode node, final ObjectCodec codec) + throws IOException, EdmPrimitiveTypeException { + + EdmTypeInfo typeInfo = StringUtils.isBlank(valuable.getType()) ? null + : new EdmTypeInfo.Builder().setTypeExpression(valuable.getType()).build(); + + final Map.Entry<ODataPropertyType, EdmTypeInfo> guessed = guessPropertyType(node); + if (typeInfo == null) { + typeInfo = guessed.getValue(); + } + + final ODataPropertyType propType = typeInfo == null ? guessed.getKey() + : typeInfo.isCollection() ? ODataPropertyType.COLLECTION + : typeInfo.isPrimitiveType() ? ODataPropertyType.PRIMITIVE + : node.isValueNode() ? ODataPropertyType.ENUM : ODataPropertyType.COMPLEX; + + switch (propType) { + case COLLECTION: + fromCollection(valuable, node.elements(), typeInfo, codec); + break; + + case COMPLEX: + if (node.has(Constants.JSON_TYPE)) { + valuable.setType(node.get(Constants.JSON_TYPE).asText()); + ((ObjectNode) node).remove(Constants.JSON_TYPE); + } + final Object value = fromComplex((ObjectNode) node, codec); + valuable.setValue(ValueType.COMPLEX, value); + break; + + case ENUM: + valuable.setValue(ValueType.ENUM, node.asText()); + break; + + case PRIMITIVE: + if (valuable.getType() == null && typeInfo != null) { + valuable.setType(typeInfo.getFullQualifiedName().toString()); + } + final Object primitiveValue = fromPrimitive(node, typeInfo); + valuable.setValue(primitiveValue instanceof Geospatial ? ValueType.GEOSPATIAL : ValueType.PRIMITIVE, + primitiveValue); + break; + + case EMPTY: + default: + valuable.setValue(ValueType.PRIMITIVE, StringUtils.EMPTY); + } + } + + @Override + public ResWrap<EntityCollection> toEntitySet(final InputStream input) throws ODataDeserializerException { + try { + parser = new JsonFactory(new ObjectMapper()).createParser(input); + return new JsonEntitySetDeserializer(serverMode).doDeserialize(parser); + } catch (final IOException e) { + throw new ODataDeserializerException(e); + } + } + + @Override + public ResWrap<Entity> toEntity(final InputStream input) throws ODataDeserializerException { + try { + parser = new JsonFactory(new ObjectMapper()).createParser(input); + return new JsonEntityDeserializer(serverMode).doDeserialize(parser); + } catch (final IOException e) { + throw new ODataDeserializerException(e); + } + } + + @Override + public ResWrap<Property> toProperty(final InputStream input) throws ODataDeserializerException { + try { + parser = new JsonFactory(new ObjectMapper()).createParser(input); + return new JsonPropertyDeserializer(serverMode).doDeserialize(parser); + } catch (final IOException e) { + throw new ODataDeserializerException(e); + } + } + + @Override + public ODataError toError(final InputStream input) throws ODataDeserializerException { + try { + parser = new JsonFactory(new ObjectMapper()).createParser(input); + return new JsonODataErrorDeserializer(serverMode).doDeserialize(parser); + } catch (final IOException e) { + throw new ODataDeserializerException(e); + } + } +} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/20351253/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonEntityDeserializer.java ---------------------------------------------------------------------- diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonEntityDeserializer.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonEntityDeserializer.java new file mode 100644 index 0000000..258c028 --- /dev/null +++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonEntityDeserializer.java @@ -0,0 +1,229 @@ +/* + * 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.serialization; + +import java.io.IOException; +import java.net.URI; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Matcher; + +import org.apache.commons.lang3.StringUtils; +import org.apache.olingo.commons.api.Constants; +import org.apache.olingo.commons.api.data.Annotation; +import org.apache.olingo.commons.api.data.Entity; +import org.apache.olingo.commons.api.data.Link; +import org.apache.olingo.commons.api.data.Operation; +import org.apache.olingo.commons.api.data.ResWrap; +import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException; +import org.apache.olingo.commons.core.edm.EdmTypeInfo; + +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; + +/** + * Reads JSON string into an entity. + * <br/> + * If metadata information is available, the corresponding entity fields and content will be populated. + */ +public class JsonEntityDeserializer extends JsonDeserializer { + + public JsonEntityDeserializer(final boolean serverMode) { + super(serverMode); + } + + protected ResWrap<Entity> doDeserialize(final JsonParser parser) throws IOException { + + final ObjectNode tree = parser.getCodec().readTree(parser); + + if (tree.has(Constants.VALUE) && tree.get(Constants.VALUE).isArray()) { + throw new JsonParseException("Expected OData Entity, found EntitySet", parser.getCurrentLocation()); + } + + final Entity entity = new Entity(); + + final URI contextURL; + if (tree.hasNonNull(Constants.JSON_CONTEXT)) { + contextURL = URI.create(tree.get(Constants.JSON_CONTEXT).textValue()); + tree.remove(Constants.JSON_CONTEXT); + } else if (tree.hasNonNull(Constants.JSON_METADATA)) { + contextURL = URI.create(tree.get(Constants.JSON_METADATA).textValue()); + tree.remove(Constants.JSON_METADATA); + } else { + contextURL = null; + } + if (contextURL != null) { + entity.setBaseURI(URI.create(StringUtils.substringBefore(contextURL.toASCIIString(), Constants.METADATA))); + } + + final String metadataETag; + if (tree.hasNonNull(Constants.JSON_METADATA_ETAG)) { + metadataETag = tree.get(Constants.JSON_METADATA_ETAG).textValue(); + tree.remove(Constants.JSON_METADATA_ETAG); + } else { + metadataETag = null; + } + + if (tree.hasNonNull(Constants.JSON_ETAG)) { + entity.setETag(tree.get(Constants.JSON_ETAG).textValue()); + tree.remove(Constants.JSON_ETAG); + } + + if (tree.hasNonNull(Constants.JSON_TYPE)) { + entity.setType(new EdmTypeInfo.Builder().setTypeExpression(tree.get(Constants.JSON_TYPE).textValue()).build() + .internal()); + tree.remove(Constants.JSON_TYPE); + } + + if (tree.hasNonNull(Constants.JSON_ID)) { + entity.setId(URI.create(tree.get(Constants.JSON_ID).textValue())); + tree.remove(Constants.JSON_ID); + } + + if (tree.hasNonNull(Constants.JSON_READ_LINK)) { + final Link link = new Link(); + link.setRel(Constants.SELF_LINK_REL); + link.setHref(tree.get(Constants.JSON_READ_LINK).textValue()); + entity.setSelfLink(link); + + tree.remove(Constants.JSON_READ_LINK); + } + + if (tree.hasNonNull(Constants.JSON_EDIT_LINK)) { + final Link link = new Link(); + if (serverMode) { + link.setRel(Constants.EDIT_LINK_REL); + } + link.setHref(tree.get(Constants.JSON_EDIT_LINK).textValue()); + entity.setEditLink(link); + + tree.remove(Constants.JSON_EDIT_LINK); + } + + if (tree.hasNonNull(Constants.JSON_MEDIA_READ_LINK)) { + entity.setMediaContentSource(URI.create(tree.get(Constants.JSON_MEDIA_READ_LINK).textValue())); + tree.remove(Constants.JSON_MEDIA_READ_LINK); + } + if (tree.hasNonNull(Constants.JSON_MEDIA_EDIT_LINK)) { + entity.setMediaContentSource(URI.create(tree.get(Constants.JSON_MEDIA_EDIT_LINK).textValue())); + tree.remove(Constants.JSON_MEDIA_EDIT_LINK); + } + if (tree.hasNonNull(Constants.JSON_MEDIA_CONTENT_TYPE)) { + entity.setMediaContentType(tree.get(Constants.JSON_MEDIA_CONTENT_TYPE).textValue()); + tree.remove(Constants.JSON_MEDIA_CONTENT_TYPE); + } + if (tree.hasNonNull(Constants.JSON_MEDIA_ETAG)) { + entity.setMediaETag(tree.get(Constants.JSON_MEDIA_ETAG).textValue()); + tree.remove(Constants.JSON_MEDIA_ETAG); + } + + final Set<String> toRemove = new HashSet<String>(); + + final Map<String, List<Annotation>> annotations = new HashMap<String, List<Annotation>>(); + for (final Iterator<Map.Entry<String, JsonNode>> itor = tree.fields(); itor.hasNext();) { + final Map.Entry<String, JsonNode> field = itor.next(); + final Matcher customAnnotation = CUSTOM_ANNOTATION.matcher(field.getKey()); + + links(field, entity, toRemove, tree, parser.getCodec()); + if (field.getKey().endsWith(getJSONAnnotation(Constants.JSON_MEDIA_EDIT_LINK))) { + final Link link = new Link(); + link.setTitle(getTitle(field)); + link.setRel(Constants.NS_MEDIA_EDIT_LINK_REL + getTitle(field)); + link.setHref(field.getValue().textValue()); + link.setType(Constants.MEDIA_EDIT_LINK_TYPE); + entity.getMediaEditLinks().add(link); + + if (tree.has(link.getTitle() + getJSONAnnotation(Constants.JSON_MEDIA_ETAG))) { + link.setMediaETag(tree.get(link.getTitle() + getJSONAnnotation(Constants.JSON_MEDIA_ETAG)).asText()); + toRemove.add(link.getTitle() + getJSONAnnotation(Constants.JSON_MEDIA_ETAG)); + } + + toRemove.add(field.getKey()); + toRemove.add(setInline(field.getKey(), getJSONAnnotation(Constants.JSON_MEDIA_EDIT_LINK), tree, parser + .getCodec(), link)); + } else if (field.getKey().endsWith(getJSONAnnotation(Constants.JSON_MEDIA_CONTENT_TYPE))) { + final String linkTitle = getTitle(field); + for (Link link : entity.getMediaEditLinks()) { + if (linkTitle.equals(link.getTitle())) { + link.setType(field.getValue().asText()); + } + } + toRemove.add(field.getKey()); + } else if (field.getKey().charAt(0) == '#') { + final Operation operation = new Operation(); + 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())); + + entity.getOperations().add(operation); + + toRemove.add(field.getKey()); + } else if (customAnnotation.matches() && !"odata".equals(customAnnotation.group(2))) { + final Annotation annotation = new Annotation(); + annotation.setTerm(customAnnotation.group(2) + "." + customAnnotation.group(3)); + try { + value(annotation, field.getValue(), parser.getCodec()); + } catch (final EdmPrimitiveTypeException e) { + throw new IOException(e); + } + + if (!annotations.containsKey(customAnnotation.group(1))) { + annotations.put(customAnnotation.group(1), new ArrayList<Annotation>()); + } + annotations.get(customAnnotation.group(1)).add(annotation); + } + } + + for (Link link : entity.getNavigationLinks()) { + if (annotations.containsKey(link.getTitle())) { + link.getAnnotations().addAll(annotations.get(link.getTitle())); + for (Annotation annotation : annotations.get(link.getTitle())) { + toRemove.add(link.getTitle() + "@" + annotation.getTerm()); + } + } + } + for (Link link : entity.getMediaEditLinks()) { + if (annotations.containsKey(link.getTitle())) { + link.getAnnotations().addAll(annotations.get(link.getTitle())); + for (Annotation annotation : annotations.get(link.getTitle())) { + toRemove.add(link.getTitle() + "@" + annotation.getTerm()); + } + } + } + + tree.remove(toRemove); + + try { + populate(entity, entity.getProperties(), tree, parser.getCodec()); + } catch (final EdmPrimitiveTypeException e) { + throw new IOException(e); + } + + return new ResWrap<Entity>(contextURL, metadataETag, entity); + } +} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/20351253/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonEntitySerializer.java ---------------------------------------------------------------------- diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonEntitySerializer.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonEntitySerializer.java new file mode 100644 index 0000000..9f2056d --- /dev/null +++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonEntitySerializer.java @@ -0,0 +1,135 @@ +/* + * 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.serialization; + +import java.io.IOException; + +import org.apache.commons.lang3.StringUtils; +import org.apache.olingo.commons.api.Constants; +import org.apache.olingo.commons.api.data.Annotation; +import org.apache.olingo.commons.api.data.Entity; +import org.apache.olingo.commons.api.data.Link; +import org.apache.olingo.commons.api.data.Operation; +import org.apache.olingo.commons.api.data.Property; +import org.apache.olingo.commons.api.data.ResWrap; +import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException; +import org.apache.olingo.commons.api.format.ODataFormat; +import org.apache.olingo.commons.core.edm.EdmTypeInfo; + +import com.fasterxml.jackson.core.JsonGenerator; + +/** + * Writes out JSON string from an entity. + */ +public class JsonEntitySerializer extends JsonSerializer { + + public JsonEntitySerializer(final boolean serverMode) { + super(serverMode); + } + + public JsonEntitySerializer(final boolean serverMode, final ODataFormat format) { + super(serverMode, format); + } + + protected void doSerialize(final Entity entity, final JsonGenerator jgen) + throws IOException, EdmPrimitiveTypeException { + + doContainerSerialize(new ResWrap<Entity>(null, null, entity), jgen); + } + + protected void doContainerSerialize(final ResWrap<Entity> container, final JsonGenerator jgen) + throws IOException, EdmPrimitiveTypeException { + + final Entity entity = container.getPayload(); + + jgen.writeStartObject(); + + if (serverMode) { + if (container.getContextURL() != null) { + jgen.writeStringField(Constants.JSON_CONTEXT, container.getContextURL().toASCIIString()); + } + if (StringUtils.isNotBlank(container.getMetadataETag())) { + jgen.writeStringField(Constants.JSON_METADATA_ETAG, container.getMetadataETag()); + } + + if (StringUtils.isNotBlank(entity.getETag())) { + jgen.writeStringField(Constants.JSON_ETAG, entity.getETag()); + } + } + + if (StringUtils.isNotBlank(entity.getType()) && format != ODataFormat.JSON_NO_METADATA) { + jgen.writeStringField(Constants.JSON_TYPE, + new EdmTypeInfo.Builder().setTypeExpression(entity.getType()).build().external()); + } + + if (entity.getId() != null && format != ODataFormat.JSON_NO_METADATA) { + jgen.writeStringField(Constants.JSON_ID, entity.getId().toASCIIString()); + } + + for (Annotation annotation : entity.getAnnotations()) { + valuable(jgen, annotation, "@" + annotation.getTerm()); + } + + for (Property property : entity.getProperties()) { + valuable(jgen, property, property.getName()); + } + + if (serverMode && entity.getEditLink() != null && StringUtils.isNotBlank(entity.getEditLink().getHref())) { + jgen.writeStringField(Constants.JSON_EDIT_LINK, + entity.getEditLink().getHref()); + + if (entity.isMediaEntity()) { + jgen.writeStringField(Constants.JSON_MEDIA_READ_LINK, + entity.getEditLink().getHref() + "/$value"); + } + } + + if (format != ODataFormat.JSON_NO_METADATA) { + links(entity, jgen); + } + + for (Link link : entity.getMediaEditLinks()) { + if (link.getTitle() == null) { + jgen.writeStringField(Constants.JSON_MEDIA_EDIT_LINK, link.getHref()); + } + + if (link.getInlineEntity() != null) { + jgen.writeObjectField(link.getTitle(), link.getInlineEntity()); + } + if (link.getInlineEntitySet() != null) { + jgen.writeArrayFieldStart(link.getTitle()); + for (Entity subEntry : link.getInlineEntitySet().getEntities()) { + jgen.writeObject(subEntry); + } + jgen.writeEndArray(); + } + } + + if (serverMode) { + for (Operation operation : entity.getOperations()) { + jgen.writeObjectFieldStart("#" + StringUtils.substringAfterLast(operation.getMetadataAnchor(), "#")); + jgen.writeStringField(Constants.ATTR_TITLE, operation.getTitle()); + jgen.writeStringField(Constants.ATTR_TARGET, operation.getTarget().toASCIIString()); + jgen.writeEndObject(); + } + } + + jgen.writeEndObject(); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/20351253/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonEntitySetDeserializer.java ---------------------------------------------------------------------- diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonEntitySetDeserializer.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonEntitySetDeserializer.java new file mode 100644 index 0000000..2fb7b25 --- /dev/null +++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonEntitySetDeserializer.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.serialization; + +import java.io.IOException; +import java.net.URI; +import java.util.Iterator; +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; +import org.apache.olingo.commons.api.Constants; +import org.apache.olingo.commons.api.data.Annotation; +import org.apache.olingo.commons.api.data.EntityCollection; +import org.apache.olingo.commons.api.data.ResWrap; +import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; + +/** + * Reads JSON string into an entity set. + * <br/> + * If metadata information is available, the corresponding entity fields and content will be populated. + */ +public class JsonEntitySetDeserializer extends JsonDeserializer { + + public JsonEntitySetDeserializer(final boolean serverMode) { + super(serverMode); + } + + protected ResWrap<EntityCollection> doDeserialize(final JsonParser parser) throws IOException { + + final ObjectNode tree = (ObjectNode) parser.getCodec().readTree(parser); + + if (!tree.has(Constants.VALUE)) { + return null; + } + + final EntityCollection entitySet = new EntityCollection(); + + URI contextURL; + if (tree.hasNonNull(Constants.JSON_CONTEXT)) { + contextURL = URI.create(tree.get(Constants.JSON_CONTEXT).textValue()); + tree.remove(Constants.JSON_CONTEXT); + } else if (tree.hasNonNull(Constants.JSON_METADATA)) { + contextURL = URI.create(tree.get(Constants.JSON_METADATA).textValue()); + tree.remove(Constants.JSON_METADATA); + } else { + contextURL = null; + } + if (contextURL != null) { + entitySet.setBaseURI(URI.create(StringUtils.substringBefore(contextURL.toASCIIString(), Constants.METADATA))); + } + + final String metadataETag; + if (tree.hasNonNull(Constants.JSON_METADATA_ETAG)) { + metadataETag = tree.get(Constants.JSON_METADATA_ETAG).textValue(); + tree.remove(Constants.JSON_METADATA_ETAG); + } else { + metadataETag = null; + } + + if (tree.hasNonNull(Constants.JSON_COUNT)) { + entitySet.setCount(tree.get(Constants.JSON_COUNT).asInt()); + tree.remove(Constants.JSON_COUNT); + } + if (tree.hasNonNull(Constants.JSON_NEXT_LINK)) { + entitySet.setNext(URI.create(tree.get(Constants.JSON_NEXT_LINK).textValue())); + tree.remove(Constants.JSON_NEXT_LINK); + } + if (tree.hasNonNull(Constants.JSON_DELTA_LINK)) { + entitySet.setDeltaLink(URI.create(tree.get(Constants.JSON_DELTA_LINK).textValue())); + tree.remove(Constants.JSON_DELTA_LINK); + } + + if (tree.hasNonNull(Constants.VALUE)) { + final JsonEntityDeserializer entityDeserializer = new JsonEntityDeserializer(serverMode); + for (JsonNode jsonNode : tree.get(Constants.VALUE)) { + entitySet.getEntities().add( + entityDeserializer.doDeserialize(jsonNode.traverse(parser.getCodec())).getPayload()); + } + tree.remove(Constants.VALUE); + } + + // any remaining entry is supposed to be an annotation or is ignored + for (final Iterator<Map.Entry<String, JsonNode>> itor = tree.fields(); itor.hasNext();) { + final Map.Entry<String, JsonNode> field = itor.next(); + if (field.getKey().charAt(0) == '@') { + final Annotation annotation = new Annotation(); + annotation.setTerm(field.getKey().substring(1)); + + try { + value(annotation, field.getValue(), parser.getCodec()); + } catch (final EdmPrimitiveTypeException e) { + throw new IOException(e); + } + entitySet.getAnnotations().add(annotation); + } + } + + return new ResWrap<EntityCollection>(contextURL, metadataETag, entitySet); + } +} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/20351253/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonEntitySetSerializer.java ---------------------------------------------------------------------- diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonEntitySetSerializer.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonEntitySetSerializer.java new file mode 100644 index 0000000..6bcf822 --- /dev/null +++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonEntitySetSerializer.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.client.core.serialization; + +import java.io.IOException; + +import org.apache.commons.lang3.StringUtils; +import org.apache.olingo.commons.api.Constants; +import org.apache.olingo.commons.api.data.Annotation; +import org.apache.olingo.commons.api.data.Entity; +import org.apache.olingo.commons.api.data.EntityCollection; +import org.apache.olingo.commons.api.data.ResWrap; +import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException; + +import com.fasterxml.jackson.core.JsonGenerator; + +public class JsonEntitySetSerializer extends JsonSerializer { + + public JsonEntitySetSerializer(final boolean serverMode) { + super(serverMode); + } + + protected void doSerialize(final EntityCollection entitySet, final JsonGenerator jgen) + throws IOException, EdmPrimitiveTypeException { + doContainerSerialize(new ResWrap<EntityCollection>(null, null, entitySet), jgen); + } + + protected void doContainerSerialize(final ResWrap<EntityCollection> container, final JsonGenerator jgen) + throws IOException, EdmPrimitiveTypeException { + + final EntityCollection entitySet = container.getPayload(); + + jgen.writeStartObject(); + + if (serverMode) { + if (container.getContextURL() != null) { + jgen.writeStringField(Constants.JSON_CONTEXT, container.getContextURL().toASCIIString()); + } + + if (StringUtils.isNotBlank(container.getMetadataETag())) { + jgen.writeStringField( + Constants.JSON_METADATA_ETAG, + container.getMetadataETag()); + } + } + + if (entitySet.getId() != null) { + jgen.writeStringField(Constants.JSON_ID, entitySet.getId().toASCIIString()); + } + jgen.writeNumberField(Constants.JSON_COUNT, + entitySet.getCount() == null ? entitySet.getEntities().size() : entitySet.getCount()); + if (serverMode) { + if (entitySet.getNext() != null) { + jgen.writeStringField(Constants.JSON_NEXT_LINK, + entitySet.getNext().toASCIIString()); + } + if (entitySet.getDeltaLink() != null) { + jgen.writeStringField(Constants.JSON_DELTA_LINK, + entitySet.getDeltaLink().toASCIIString()); + } + } + + for (Annotation annotation : entitySet.getAnnotations()) { + valuable(jgen, annotation, "@" + annotation.getTerm()); + } + + jgen.writeArrayFieldStart(Constants.VALUE); + final JsonEntitySerializer entitySerializer = new JsonEntitySerializer(serverMode); + for (Entity entity : entitySet.getEntities()) { + entitySerializer.doSerialize(entity, jgen); + } + jgen.writeEndArray(); + + jgen.writeEndObject(); + } +} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/20351253/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonGeoValueDeserializer.java ---------------------------------------------------------------------- diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonGeoValueDeserializer.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonGeoValueDeserializer.java new file mode 100644 index 0000000..75aab4a --- /dev/null +++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonGeoValueDeserializer.java @@ -0,0 +1,262 @@ +/* + * 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.serialization; + +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.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.api.edm.geo.SRID; +import org.apache.olingo.commons.core.edm.EdmTypeInfo; +import org.apache.olingo.commons.core.edm.primitivetype.EdmDouble; + +import com.fasterxml.jackson.databind.JsonNode; + +class JsonGeoValueDeserializer { + + private Point point(final Iterator<JsonNode> itor, final EdmPrimitiveTypeKind type, final SRID srid) { + Point point = null; + + if (itor.hasNext()) { + point = new Point(GeoUtils.getDimension(type), srid); + 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 SRID srid) { + final MultiPoint multiPoint; + + 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, srid)); + } + multiPoint = new MultiPoint(GeoUtils.getDimension(type), srid, points); + } else { + multiPoint = new MultiPoint(GeoUtils.getDimension(type), srid, Collections.<Point> emptyList()); + } + + return multiPoint; + } + + private LineString lineString(final Iterator<JsonNode> itor, final EdmPrimitiveTypeKind type, final SRID srid) { + final LineString lineString; + + 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, srid)); + } + lineString = new LineString(GeoUtils.getDimension(type), srid, points); + } else { + lineString = new LineString(GeoUtils.getDimension(type), srid, Collections.<Point> emptyList()); + } + + return lineString; + } + + private MultiLineString multiLineString(final Iterator<JsonNode> itor, final EdmPrimitiveTypeKind type, + final SRID srid) { + + final MultiLineString multiLineString; + + 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, srid)); + } + multiLineString = new MultiLineString(GeoUtils.getDimension(type), srid, lineStrings); + } else { + multiLineString = new MultiLineString(GeoUtils.getDimension(type), srid, Collections.<LineString> emptyList()); + } + + return multiLineString; + } + + private Polygon polygon(final Iterator<JsonNode> itor, final EdmPrimitiveTypeKind type, final SRID srid) { + 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, srid)); + } + } + } + + 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, srid)); + } + } + } + + return new Polygon(GeoUtils.getDimension(type), srid, intPoints, extPoints); + } + + private MultiPolygon multiPolygon(final Iterator<JsonNode> itor, final EdmPrimitiveTypeKind type, final SRID srid) { + final MultiPolygon multiPolygon; + + 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, srid)); + } + multiPolygon = new MultiPolygon(GeoUtils.getDimension(type), srid, polygons); + } else { + multiPolygon = new MultiPolygon(GeoUtils.getDimension(type), srid, Collections.<Polygon> emptyList()); + } + + return multiPolygon; + } + + private GeospatialCollection collection(final Iterator<JsonNode> itor, final EdmPrimitiveTypeKind type, + final SRID srid) { + + final GeospatialCollection collection; + + 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), srid, geospatials); + } else { + collection = new GeospatialCollection(GeoUtils.getDimension(type), srid, 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(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(); + + SRID srid = null; + if (node.has(Constants.JSON_CRS)) { + srid = SRID.valueOf( + 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, srid); + break; + + case GeographyMultiPoint: + case GeometryMultiPoint: + value = multipoint(cooItor, actualType, srid); + break; + + case GeographyLineString: + case GeometryLineString: + value = lineString(cooItor, actualType, srid); + break; + + case GeographyMultiLineString: + case GeometryMultiLineString: + value = multiLineString(cooItor, actualType, srid); + break; + + case GeographyPolygon: + case GeometryPolygon: + value = polygon(cooItor, actualType, srid); + break; + + case GeographyMultiPolygon: + case GeometryMultiPolygon: + value = multiPolygon(cooItor, actualType, srid); + break; + + case GeographyCollection: + case GeometryCollection: + value = collection(node.get(Constants.JSON_GEOMETRIES).elements(), actualType, srid); + break; + + default: + } + + return value; + } +} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/20351253/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonGeoValueSerializer.java ---------------------------------------------------------------------- diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonGeoValueSerializer.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonGeoValueSerializer.java new file mode 100644 index 0000000..12ac4f5 --- /dev/null +++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonGeoValueSerializer.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.serialization; + +import java.io.IOException; + +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.api.edm.geo.SRID; +import org.apache.olingo.commons.core.edm.primitivetype.EdmDouble; + +import com.fasterxml.jackson.core.JsonGenerator; + +class JsonGeoValueSerializer { + + private void srid(final JsonGenerator jgen, final SRID srid) 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:" + srid.toString()); + 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 (Point point : multiPoint) { + jgen.writeStartArray(); + point(jgen, point); + jgen.writeEndArray(); + } + } + + private void lineString(final JsonGenerator jgen, final ComposedGeospatial<Point> lineString) throws IOException { + for (Point point : lineString) { + jgen.writeStartArray(); + point(jgen, point); + jgen.writeEndArray(); + } + } + + private void multiLineString(final JsonGenerator jgen, final MultiLineString multiLineString) throws IOException { + for (LineString lineString : multiLineString) { + jgen.writeStartArray(); + lineString(jgen, lineString); + 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 (Polygon polygon : multiPolygon) { + jgen.writeStartArray(); + polygon(jgen, polygon); + jgen.writeEndArray(); + } + } + + private void collection(final JsonGenerator jgen, final GeospatialCollection collection) throws IOException { + jgen.writeArrayFieldStart(Constants.JSON_GEOMETRIES); + for (Geospatial geospatial : collection) { + jgen.writeStartObject(); + serialize(jgen, geospatial); + 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.getSrid() != null && value.getSrid().isNotDefault()) { + srid(jgen, value.getSrid()); + } + } + +} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/20351253/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonODataErrorDeserializer.java ---------------------------------------------------------------------- diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonODataErrorDeserializer.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonODataErrorDeserializer.java new file mode 100644 index 0000000..2d1f59f --- /dev/null +++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonODataErrorDeserializer.java @@ -0,0 +1,87 @@ +/* + * 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.serialization; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; + +import org.apache.olingo.commons.api.Constants; +import org.apache.olingo.commons.api.ODataError; +import org.apache.olingo.commons.api.ODataErrorDetail; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; + +public class JsonODataErrorDeserializer extends JsonDeserializer { + + public JsonODataErrorDeserializer(final boolean serverMode) { + super(serverMode); + } + + protected ODataError doDeserialize(final JsonParser parser) throws IOException { + + final ODataError error = new ODataError(); + + final ObjectNode tree = parser.getCodec().readTree(parser); + if (tree.has(Constants.JSON_ERROR)) { + final JsonNode errorNode = tree.get(Constants.JSON_ERROR); + + if (errorNode.has(Constants.ERROR_CODE)) { + error.setCode(errorNode.get(Constants.ERROR_CODE).textValue()); + } + if (errorNode.has(Constants.ERROR_MESSAGE)) { + final JsonNode message = errorNode.get(Constants.ERROR_MESSAGE); + if (message.isValueNode()) { + error.setMessage(message.textValue()); + } else if (message.isObject()) { + error.setMessage(message.get(Constants.VALUE).asText()); + } + } + if (errorNode.has(Constants.ERROR_TARGET)) { + error.setTarget(errorNode.get(Constants.ERROR_TARGET).textValue()); + } + if (errorNode.hasNonNull(Constants.ERROR_DETAILS)) { + List<ODataErrorDetail> details = new ArrayList<ODataErrorDetail>(); + JsonODataErrorDetailDeserializer detailDeserializer = new JsonODataErrorDetailDeserializer(serverMode); + for (JsonNode jsonNode : errorNode.get(Constants.ERROR_DETAILS)) { + details.add(detailDeserializer.doDeserialize(jsonNode.traverse(parser.getCodec())) + .getPayload()); + } + + error.setDetails(details); + } + if (errorNode.hasNonNull(Constants.ERROR_INNERERROR)) { + HashMap<String, String> innerErrorMap = new HashMap<String, String>(); + final JsonNode innerError = errorNode.get(Constants.ERROR_INNERERROR); + for (final Iterator<String> itor = innerError.fieldNames(); itor.hasNext();) { + final String keyTmp = itor.next(); + final String val = innerError.get(keyTmp).toString(); + innerErrorMap.put(keyTmp, val); + } + error.setInnerError(innerErrorMap); + } + } + + return error; + } +} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/20351253/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonODataErrorDetailDeserializer.java ---------------------------------------------------------------------- diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonODataErrorDetailDeserializer.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonODataErrorDetailDeserializer.java new file mode 100644 index 0000000..27a8eb9 --- /dev/null +++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonODataErrorDetailDeserializer.java @@ -0,0 +1,58 @@ +/* + * 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.serialization; + +import java.io.IOException; +import java.net.URI; + +import org.apache.olingo.commons.api.Constants; +import org.apache.olingo.commons.api.ODataErrorDetail; +import org.apache.olingo.commons.api.data.ResWrap; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.JsonNode; + +public class JsonODataErrorDetailDeserializer extends JsonDeserializer { + + public JsonODataErrorDetailDeserializer(final boolean serverMode) { + super(serverMode); + } + + protected ResWrap<ODataErrorDetail> doDeserialize(final JsonParser parser) throws IOException { + + final ODataErrorDetail error = new ODataErrorDetail(); + final JsonNode errorNode = parser.getCodec().readTree(parser); + if (errorNode.has(Constants.ERROR_CODE)) { + error.setCode(errorNode.get(Constants.ERROR_CODE).textValue()); + } + if (errorNode.has(Constants.ERROR_MESSAGE)) { + final JsonNode message = errorNode.get(Constants.ERROR_MESSAGE); + if (message.isValueNode()) { + error.setMessage(message.textValue()); + } else if (message.isObject()) { + error.setMessage(message.get(Constants.VALUE).asText()); + } + } + if (errorNode.has(Constants.ERROR_TARGET)) { + error.setTarget(errorNode.get(Constants.ERROR_TARGET).textValue()); + } + + return new ResWrap<ODataErrorDetail>((URI) null, null, error); + } +} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/20351253/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonPropertyDeserializer.java ---------------------------------------------------------------------- diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonPropertyDeserializer.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonPropertyDeserializer.java new file mode 100644 index 0000000..3a59650 --- /dev/null +++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonPropertyDeserializer.java @@ -0,0 +1,114 @@ +/* + * 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.serialization; + +import java.io.IOException; +import java.net.URI; +import java.util.Iterator; +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; +import org.apache.olingo.commons.api.Constants; +import org.apache.olingo.commons.api.data.Annotation; +import org.apache.olingo.commons.api.data.Property; +import org.apache.olingo.commons.api.data.ResWrap; +import org.apache.olingo.commons.api.data.ValueType; +import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException; +import org.apache.olingo.commons.core.edm.EdmTypeInfo; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; + +/** + * Parse JSON string into <tt>Property</tt>. + */ +public class JsonPropertyDeserializer extends JsonDeserializer { + + public JsonPropertyDeserializer(final boolean serverMode) { + super(serverMode); + } + + protected ResWrap<Property> doDeserialize(final JsonParser parser) throws IOException { + + final ObjectNode tree = parser.getCodec().readTree(parser); + + final String metadataETag; + final URI contextURL; + final Property property = new Property(); + + if (tree.hasNonNull(Constants.JSON_METADATA_ETAG)) { + metadataETag = tree.get(Constants.JSON_METADATA_ETAG).textValue(); + tree.remove(Constants.JSON_METADATA_ETAG); + } else { + metadataETag = null; + } + + if (tree.hasNonNull(Constants.JSON_CONTEXT)) { + contextURL = URI.create(tree.get(Constants.JSON_CONTEXT).textValue()); + property.setName(StringUtils.substringAfterLast(contextURL.toASCIIString(), "/")); + tree.remove(Constants.JSON_CONTEXT); + } else if (tree.hasNonNull(Constants.JSON_METADATA)) { + contextURL = URI.create(tree.get(Constants.JSON_METADATA).textValue()); + property.setType(new EdmTypeInfo.Builder(). + setTypeExpression(StringUtils.substringAfterLast(contextURL.toASCIIString(), "#")).build().internal()); + tree.remove(Constants.JSON_METADATA); + } else { + contextURL = null; + } + + if (tree.has(Constants.JSON_TYPE)) { + property.setType(new EdmTypeInfo.Builder().setTypeExpression(tree.get(Constants.JSON_TYPE).textValue()).build() + .internal()); + tree.remove(Constants.JSON_TYPE); + } + + if (tree.has(Constants.JSON_NULL) && tree.get(Constants.JSON_NULL).asBoolean()) { + property.setValue(ValueType.PRIMITIVE, null); + tree.remove(Constants.JSON_NULL); + } + + if (property.getValue() == null) { + try { + value(property, tree.has(Constants.VALUE) ? tree.get(Constants.VALUE) : tree, parser.getCodec()); + } catch (final EdmPrimitiveTypeException e) { + throw new IOException(e); + } + tree.remove(Constants.VALUE); + } + + // any remaining entry is supposed to be an annotation or is ignored + for (final Iterator<Map.Entry<String, JsonNode>> itor = tree.fields(); itor.hasNext();) { + final Map.Entry<String, JsonNode> field = itor.next(); + if (field.getKey().charAt(0) == '@') { + final Annotation annotation = new Annotation(); + annotation.setTerm(field.getKey().substring(1)); + + try { + value(annotation, field.getValue(), parser.getCodec()); + } catch (final EdmPrimitiveTypeException e) { + throw new IOException(e); + } + property.getAnnotations().add(annotation); + } + } + + return new ResWrap<Property>(contextURL, metadataETag, property); + } +} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/20351253/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonPropertySerializer.java ---------------------------------------------------------------------- diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonPropertySerializer.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonPropertySerializer.java new file mode 100644 index 0000000..11c6821 --- /dev/null +++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/JsonPropertySerializer.java @@ -0,0 +1,93 @@ +/* + * 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.serialization; + +import java.io.IOException; +import java.net.URI; + +import org.apache.commons.lang3.StringUtils; +import org.apache.olingo.commons.api.Constants; +import org.apache.olingo.commons.api.data.Annotation; +import org.apache.olingo.commons.api.data.Property; +import org.apache.olingo.commons.api.data.ResWrap; +import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException; +import org.apache.olingo.commons.core.edm.EdmTypeInfo; + +import com.fasterxml.jackson.core.JsonGenerator; + +/** + * Writes out JSON string from <tt>PropertyImpl</tt>. + */ +public class JsonPropertySerializer extends JsonSerializer { + + public JsonPropertySerializer(final boolean serverMode) { + super(serverMode); + } + + protected void doSerialize(final Property property, final JsonGenerator jgen) + throws IOException, EdmPrimitiveTypeException { + doContainerSerialize(new ResWrap<Property>((URI) null, null, property), jgen); + } + + protected void doContainerSerialize(final ResWrap<Property> container, final JsonGenerator jgen) + throws IOException, EdmPrimitiveTypeException { + + final Property property = container.getPayload(); + + jgen.writeStartObject(); + + if (serverMode && container.getContextURL() != null) { + jgen.writeStringField(Constants.JSON_CONTEXT, container.getContextURL().toASCIIString()); + } + + if (StringUtils.isNotBlank(property.getType())) { + jgen.writeStringField(Constants.JSON_TYPE, + new EdmTypeInfo.Builder().setTypeExpression(property.getType()).build().external()); + } + + for (Annotation annotation : property.getAnnotations()) { + valuable(jgen, annotation, "@" + annotation.getTerm()); + } + + if (property.isNull()) { + jgen.writeBooleanField(Constants.JSON_NULL, true); + } else if (property.isGeospatial() || property.isCollection()) { + valuable(jgen, property, Constants.VALUE); + } else if (property.isPrimitive()) { + final EdmTypeInfo typeInfo = property.getType() == null + ? null + : new EdmTypeInfo.Builder().setTypeExpression(property.getType()).build(); + + jgen.writeFieldName(Constants.VALUE); + primitiveValue(jgen, typeInfo, property.asPrimitive()); + } else if (property.isEnum()) { + jgen.writeStringField(Constants.VALUE, property.asEnum().toString()); + } else if (property.isComplex()) { + for (Property cproperty : property.asComplex().getValue()) { + valuable(jgen, cproperty, cproperty.getName()); + } + } else if (property.isComplex()) { + for (Property cproperty : property.asComplex().getValue()) { + valuable(jgen, cproperty, cproperty.getName()); + } + } + + jgen.writeEndObject(); + } +}
