diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/OData.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/OData.java index 47dcd8f61..eddd0da53 100644 --- a/lib/server-api/src/main/java/org/apache/olingo/server/api/OData.java +++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/OData.java @@ -22,7 +22,9 @@ import java.util.List; import org.apache.olingo.commons.api.ODataRuntimeException; import org.apache.olingo.commons.api.format.ODataFormat; +import org.apache.olingo.server.api.deserializer.DeserializerException; import org.apache.olingo.server.api.deserializer.FixedFormatDeserializer; +import org.apache.olingo.server.api.deserializer.ODataDeserializer; import org.apache.olingo.server.api.edm.provider.EdmProvider; import org.apache.olingo.server.api.edmx.EdmxReference; import org.apache.olingo.server.api.serializer.FixedFormatSerializer; @@ -96,4 +98,6 @@ public abstract class OData { * It can be used in Processor implementations. */ public abstract UriHelper createUriHelper(); + + public abstract ODataDeserializer createDeserializer(ODataFormat format) throws DeserializerException; } diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/deserializer/DeserializerException.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/deserializer/DeserializerException.java index 2e4e7bc27..247512566 100644 --- a/lib/server-api/src/main/java/org/apache/olingo/server/api/deserializer/DeserializerException.java +++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/deserializer/DeserializerException.java @@ -28,7 +28,18 @@ public class DeserializerException extends ODataTranslatedException { /** Keys for exception texts in the resource bundle. */ public static enum MessageKeys implements MessageKey { NOT_IMPLEMENTED, - IO_EXCEPTION; + IO_EXCEPTION, + //TODO: create texts for the following message keys: + /** parameter: format */ UNSUPPORTED_FORMAT, + JSON_SYNTAX_EXCEPTION, + /** parameter: propertyName */ INVALID_NULL_PROPERTY, + TREE_NOT_EMPTY, + /** parameter: propertyName */ INVALID_VALUE_FOR_PROPERTY, + /** parameter: propertyName */ INVALID_TYPE_FOR_PROPERTY, + VALUE_ARRAY_NOT_PRESENT, + VALUE_TAG_MUST_BE_AN_ARRAY, + INVALID_ENTITY, + /** parameter: navigationPropertyName */INVALID_VALUE_FOR_NAVIGATION_PROPERTY; @Override public String getKey() { diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/deserializer/ODataDeserializer.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/deserializer/ODataDeserializer.java new file mode 100644 index 000000000..a3122216b --- /dev/null +++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/deserializer/ODataDeserializer.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.olingo.server.api.deserializer; + +import java.io.InputStream; + +import org.apache.olingo.commons.api.data.Entity; +import org.apache.olingo.commons.api.data.EntitySet; +import org.apache.olingo.commons.api.edm.EdmEntityType; + +public interface ODataDeserializer { + + Entity entity(InputStream stream, EdmEntityType edmEntityType) throws DeserializerException; + + EntitySet entitySet(InputStream stream, EdmEntityType edmEntityType) throws DeserializerException; + +} diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataImpl.java index 1a8bfcaf5..15514616b 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataImpl.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataImpl.java @@ -25,7 +25,9 @@ import org.apache.olingo.commons.api.format.ODataFormat; import org.apache.olingo.server.api.OData; import org.apache.olingo.server.api.ODataHttpHandler; import org.apache.olingo.server.api.ServiceMetadata; +import org.apache.olingo.server.api.deserializer.DeserializerException; import org.apache.olingo.server.api.deserializer.FixedFormatDeserializer; +import org.apache.olingo.server.api.deserializer.ODataDeserializer; import org.apache.olingo.server.api.edm.provider.EdmProvider; import org.apache.olingo.server.api.edmx.EdmxReference; import org.apache.olingo.server.api.serializer.FixedFormatSerializer; @@ -33,6 +35,7 @@ import org.apache.olingo.server.api.serializer.ODataSerializer; import org.apache.olingo.server.api.serializer.SerializerException; import org.apache.olingo.server.api.uri.UriHelper; import org.apache.olingo.server.core.deserializer.FixedFormatDeserializerImpl; +import org.apache.olingo.server.core.deserializer.json.ODataJsonDeserializer; import org.apache.olingo.server.core.serializer.FixedFormatSerializerImpl; import org.apache.olingo.server.core.serializer.json.ODataJsonSerializer; import org.apache.olingo.server.core.serializer.xml.ODataXmlSerializerImpl; @@ -84,4 +87,23 @@ public class ODataImpl extends OData { public UriHelper createUriHelper() { return new UriHelperImpl(); } + + @Override + public ODataDeserializer createDeserializer(ODataFormat format) throws DeserializerException{ + ODataDeserializer serializer; + switch (format) { + case JSON: + case JSON_NO_METADATA: + case JSON_FULL_METADATA: + serializer = new ODataJsonDeserializer(); + break; + case XML: + //We do not support xml deserialization right now so this mus lead to an error + default: + throw new DeserializerException("Unsupported format: " + format, + SerializerException.MessageKeys.UNSUPPORTED_FORMAT, format.toString()); + } + + return serializer; + } } diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/json/ODataJsonDeserializer.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/json/ODataJsonDeserializer.java new file mode 100644 index 000000000..bca290a37 --- /dev/null +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/json/ODataJsonDeserializer.java @@ -0,0 +1,523 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.olingo.server.core.deserializer.json; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.commons.lang3.StringUtils; +import org.apache.olingo.commons.api.Constants; +import org.apache.olingo.commons.api.data.Entity; +import org.apache.olingo.commons.api.data.EntitySet; +import org.apache.olingo.commons.api.data.Link; +import org.apache.olingo.commons.api.data.Property; +import org.apache.olingo.commons.api.data.ValueType; +import org.apache.olingo.commons.api.domain.ODataLinkType; +import org.apache.olingo.commons.api.edm.EdmComplexType; +import org.apache.olingo.commons.api.edm.EdmEntityType; +import org.apache.olingo.commons.api.edm.EdmEnumType; +import org.apache.olingo.commons.api.edm.EdmNavigationProperty; +import org.apache.olingo.commons.api.edm.EdmPrimitiveType; +import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException; +import org.apache.olingo.commons.api.edm.EdmProperty; +import org.apache.olingo.commons.api.edm.EdmTypeDefinition; +import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion; +import org.apache.olingo.commons.core.data.EntityImpl; +import org.apache.olingo.commons.core.data.EntitySetImpl; +import org.apache.olingo.commons.core.data.LinkImpl; +import org.apache.olingo.commons.core.data.PropertyImpl; +import org.apache.olingo.commons.core.edm.EdmTypeInfo; +import org.apache.olingo.server.api.deserializer.DeserializerException; +import org.apache.olingo.server.api.deserializer.ODataDeserializer; + +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonParseException; +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 ODataJsonDeserializer implements ODataDeserializer { + + @Override + public EntitySet entitySet(InputStream stream, EdmEntityType edmEntityType) throws DeserializerException { + try { + JsonParser parser = new JsonFactory(new ObjectMapper()).createParser(stream); + final ObjectNode tree = parser.getCodec().readTree(parser); + + return consumeEntitySetNode(edmEntityType, tree); + } catch (JsonParseException e) { + throw new DeserializerException("An JsonParseException occourred", e, + DeserializerException.MessageKeys.JSON_SYNTAX_EXCEPTION); + } catch (IOException e) { + throw new DeserializerException("An IOException occourred", e, DeserializerException.MessageKeys.IO_EXCEPTION); + } + } + + private EntitySet consumeEntitySetNode(EdmEntityType edmEntityType, final ObjectNode tree) + throws DeserializerException { + EntitySetImpl entitySet = new EntitySetImpl(); + // Consume entitySet annotations + consumeODataEntitySetAnnotations(tree, entitySet); + + // Consume entities + JsonNode jsonNode = tree.get(Constants.VALUE); + if (jsonNode != null) { + if (jsonNode.isArray() == false) { + throw new DeserializerException("The content of the value tag must be an Array but is not. ", + DeserializerException.MessageKeys.VALUE_TAG_MUST_BE_AN_ARRAY); + } + + consumeEntitySetArray(entitySet, edmEntityType, jsonNode); + tree.remove(Constants.VALUE); + } else { + throw new DeserializerException("Could not find value array.", + DeserializerException.MessageKeys.VALUE_ARRAY_NOT_PRESENT); + } + + if (tree.size() != 0) { + throw new DeserializerException("Tree should be empty but still has content left.", + DeserializerException.MessageKeys.TREE_NOT_EMPTY); + } + return entitySet; + } + + private void consumeEntitySetArray(EntitySetImpl entitySet, EdmEntityType edmEntityType, JsonNode jsonNode) + throws DeserializerException { + Iterator arrayIterator = jsonNode.iterator(); + while (arrayIterator.hasNext()) { + JsonNode arrayElement = (JsonNode) arrayIterator.next(); + if (arrayElement.isContainerNode() && arrayElement.isArray()) { + throw new DeserializerException("Nested Arrays and primitive values are not allowed for an entity value.", + DeserializerException.MessageKeys.INVALID_ENTITY); + } + + entitySet.getEntities().add(consumeEntityNode(edmEntityType, (ObjectNode) arrayElement)); + } + } + + private void consumeODataEntitySetAnnotations(final ObjectNode tree, EntitySetImpl entitySet) { + 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(StringUtils.substringBefore(contextURL.toASCIIString(), Constants.METADATA)); + } + + 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); + } + } + + @Override + public Entity entity(InputStream stream, EdmEntityType edmEntityType) throws DeserializerException { + try { + JsonParser parser = new JsonFactory(new ObjectMapper()).createParser(stream); + final ObjectNode tree = parser.getCodec().readTree(parser); + + return consumeEntityNode(edmEntityType, tree); + + } catch (JsonParseException e) { + throw new DeserializerException("An JsonParseException occourred", e, + DeserializerException.MessageKeys.JSON_SYNTAX_EXCEPTION); + } catch (IOException e) { + throw new DeserializerException("An IOException occourred", e, DeserializerException.MessageKeys.IO_EXCEPTION); + } + + } + + private Entity consumeEntityNode(EdmEntityType edmEntityType, final ObjectNode tree) throws DeserializerException { + EntityImpl entity = new EntityImpl(); + entity.setType(edmEntityType.getFullQualifiedName().getFullQualifiedNameAsString()); + + // Check and consume all Properties + List propertyNames = edmEntityType.getPropertyNames(); + for (String propertyName : propertyNames) { + JsonNode jsonNode = tree.get(propertyName); + if (jsonNode != null) { + EdmProperty edmProperty = (EdmProperty) edmEntityType.getProperty(propertyName); + if (jsonNode.isNull() == true && edmProperty.isNullable() != null + && edmProperty.isNullable().booleanValue() == false) { + throw new DeserializerException("Property: " + propertyName + " must not be null.", + DeserializerException.MessageKeys.INVALID_NULL_PROPERTY, propertyName); + } + Property property = consumePropertyNode(edmProperty, jsonNode); + entity.addProperty(property); + tree.remove(propertyName); + } + } + + // Check and consume all expanded Navigation Properties + List navigationPropertyNames = edmEntityType.getNavigationPropertyNames(); + for (String navigationPropertyName : navigationPropertyNames) { + // read expanded navigation property + JsonNode jsonNode = tree.get(navigationPropertyName); + if (jsonNode != null) { + EdmNavigationProperty edmNavigationProperty = edmEntityType.getNavigationProperty(navigationPropertyName); + if (jsonNode.isNull() == true && edmNavigationProperty.isNullable() != null + && edmNavigationProperty.isNullable().booleanValue() == false) { + throw new DeserializerException("Property: " + navigationPropertyName + " must not be null.", + DeserializerException.MessageKeys.INVALID_NULL_PROPERTY, navigationPropertyName); + } + + LinkImpl link = new LinkImpl(); + link.setTitle(navigationPropertyName); + if (jsonNode.isArray() && edmNavigationProperty.isCollection()) { + link.setType(ODataLinkType.ENTITY_SET_NAVIGATION.toString()); + EntitySetImpl inlineEntitySet = new EntitySetImpl(); + consumeEntitySetArray(inlineEntitySet, edmNavigationProperty.getType(), jsonNode); + link.setInlineEntitySet(inlineEntitySet); + } else if (!jsonNode.isArray() && !jsonNode.isValueNode() && !edmNavigationProperty.isCollection()) { + link.setType(ODataLinkType.ENTITY_NAVIGATION.toString()); + if (!jsonNode.isNull()) { + Entity inlineEntity = consumeEntityNode(edmNavigationProperty.getType(), (ObjectNode) jsonNode); + link.setInlineEntity(inlineEntity); + } + } else { + throw new DeserializerException("Invalid value: " + jsonNode.getNodeType() + + " for expanded navigation porperty: " + navigationPropertyName, + DeserializerException.MessageKeys.INVALID_VALUE_FOR_NAVIGATION_PROPERTY, navigationPropertyName); + } + entity.getNavigationLinks().add(link); + tree.remove(navigationPropertyName); + } + } + + // Check and consume all Annotations + consumeODataEntityAnnotations(tree, entity, edmEntityType); + + final List toRemove = new ArrayList(); + Iterator> fieldsIterator = tree.fields(); + // TODO: Add custom annotation support + while (fieldsIterator.hasNext()) { + Map.Entry field = (Map.Entry) fieldsIterator.next(); + if (field.getKey().endsWith(Constants.JSON_NAVIGATION_LINK)) { + final Link link = new LinkImpl(); + link.setTitle(getAnnotationName(field)); + link.setRel(ODataServiceVersion.V40.getNamespace(ODataServiceVersion.NamespaceKey.NAVIGATION_LINK_REL) + + getAnnotationName(field)); + + if (field.getValue().isValueNode()) { + link.setHref(field.getValue().textValue()); + link.setType(ODataLinkType.ENTITY_NAVIGATION.toString()); + } + + entity.getNavigationLinks().add(link); + toRemove.add(field.getKey()); + } else if (field.getKey().endsWith(Constants.JSON_ASSOCIATION_LINK)) { + final LinkImpl link = new LinkImpl(); + link.setTitle(getAnnotationName(field)); + link.setRel(ODataServiceVersion.V40.getNamespace(ODataServiceVersion.NamespaceKey.ASSOCIATION_LINK_REL) + + getAnnotationName(field)); + link.setHref(field.getValue().textValue()); + link.setType(ODataLinkType.ASSOCIATION.toString()); + entity.getAssociationLinks().add(link); + + toRemove.add(field.getKey()); + } + } + + // remove here to avoid iterator issues. + tree.remove(toRemove); + + if (tree.size() != 0) { + throw new DeserializerException("Tree should be empty but still has content left.", + DeserializerException.MessageKeys.TREE_NOT_EMPTY); + } + + return entity; + } + + private String getAnnotationName(final Map.Entry entry) { + return entry.getKey().substring(0, entry.getKey().indexOf('@')); + } + + private void consumeODataEntityAnnotations(ObjectNode tree, EntityImpl entity, EdmEntityType edmEntityType) { + 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(StringUtils.substringBefore(contextURL.toASCIIString(), Constants.METADATA)); + } + + 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 LinkImpl link = new LinkImpl(); + 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 LinkImpl link = new LinkImpl(); + link.setRel(Constants.EDIT_LINK_REL); + link.setHref(tree.get(Constants.JSON_EDIT_LINK).textValue()); + entity.setEditLink(link); + + tree.remove(Constants.JSON_EDIT_LINK); + } + + // TODO: Should we check if this is a media resource? + 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); + } + + } + + private Property consumePropertyNode(EdmProperty edmProperty, JsonNode jsonNode) throws DeserializerException { + Property property = new PropertyImpl(); + property.setName(edmProperty.getName()); + property.setType(edmProperty.getType().getFullQualifiedName().getFullQualifiedNameAsString()); + if (edmProperty.isCollection()) { + if (!jsonNode.isArray()) { + throw new DeserializerException("Value for property: " + edmProperty.getName() + + " must be an arrat but is not.", DeserializerException.MessageKeys.INVALID_VALUE_FOR_PROPERTY, + edmProperty.getName()); + } + List valueArray = new ArrayList(); + Iterator iterator = jsonNode.iterator(); + switch (edmProperty.getType().getKind()) { + case PRIMITIVE: + while (iterator.hasNext()) { + JsonNode arrayElement = iterator.next(); + Object value = readPrimitiveValue(edmProperty, arrayElement); + valueArray.add(value); + } + property.setValue(ValueType.COLLECTION_PRIMITIVE, valueArray); + break; + case DEFINITION: + while (iterator.hasNext()) { + JsonNode arrayElement = iterator.next(); + Object value = readTypeDefinitionValue(edmProperty, arrayElement); + valueArray.add(value); + } + property.setValue(ValueType.COLLECTION_PRIMITIVE, valueArray); + case ENUM: + while (iterator.hasNext()) { + JsonNode arrayElement = iterator.next(); + Object value = readEnumValue(edmProperty, arrayElement); + valueArray.add(value); + } + property.setValue(ValueType.COLLECTION_ENUM, valueArray); + break; + case COMPLEX: + while (iterator.hasNext()) { + JsonNode arrayElement = iterator.next(); + // read and add all complex properties + Object value = readComplexValue(edmProperty, arrayElement); + valueArray.add(value); + // If navigationProperties are present we have to consume them and create a LinkedComplexValue Object + // TODO: Complex Type Navigation Deserialization + // read and add all annotations + // TODO: read Complex Type Annotations + // Afterwards the node must be empty + if (arrayElement.size() != 0) { + throw new DeserializerException("Tree should be empty but still has content left.", + DeserializerException.MessageKeys.TREE_NOT_EMPTY); + } + } + property.setValue(ValueType.COLLECTION_COMPLEX, valueArray); + break; + default: + throw new DeserializerException("Invalid Type Kind for a property found: " + edmProperty.getType().getKind(), + DeserializerException.MessageKeys.INVALID_TYPE_FOR_PROPERTY, edmProperty.getName()); + } + + } else { + switch (edmProperty.getType().getKind()) { + case PRIMITIVE: + Object value = readPrimitiveValue(edmProperty, jsonNode); + property.setValue(ValueType.PRIMITIVE, value); + break; + case DEFINITION: + value = readTypeDefinitionValue(edmProperty, jsonNode); + property.setValue(ValueType.PRIMITIVE, value); + case ENUM: + value = readEnumValue(edmProperty, jsonNode); + property.setValue(ValueType.PRIMITIVE, value); + break; + case COMPLEX: + // read and add all complex properties + value = readComplexValue(edmProperty, jsonNode); + property.setValue(ValueType.COMPLEX, value); + + // read and add all navigation properties + // TODO: Complex Type Navigation Deserialization + // read and add all annotations + // TODO: read Complex Type Annotations + // Afterwards the node must be empty + if (jsonNode.size() != 0) { + throw new DeserializerException("Tree should be empty but still has content left.", + DeserializerException.MessageKeys.TREE_NOT_EMPTY); + } + break; + default: + throw new DeserializerException("Invalid Type Kind for a property found: " + edmProperty.getType().getKind(), + DeserializerException.MessageKeys.INVALID_TYPE_FOR_PROPERTY, edmProperty.getName()); + } + } + return property; + } + + private Object readComplexValue(EdmProperty edmComplexProperty, JsonNode jsonNode) throws DeserializerException { + if (jsonNode.isArray() || !jsonNode.isContainerNode()) { + throw new DeserializerException( + "Inavlid value for property: " + edmComplexProperty.getName() + " must not be an array or primitive value.", + DeserializerException.MessageKeys.INVALID_VALUE_FOR_PROPERTY, edmComplexProperty.getName()); + } + // Even if there are no properties defined we have to give back an empty list + List propertyList = new ArrayList(); + EdmComplexType edmType = (EdmComplexType) edmComplexProperty.getType(); + // Check and consume all Properties + for (String propertyName : edmType.getPropertyNames()) { + EdmProperty edmProperty = (EdmProperty) edmType.getProperty(propertyName); + JsonNode subNode = jsonNode.get(propertyName); + if (subNode != null) { + if (subNode.isNull() && edmProperty.isNullable() != null && edmProperty.isNullable().booleanValue() == false) { + throw new DeserializerException("Property: " + propertyName + " must not be null.", + DeserializerException.MessageKeys.INVALID_NULL_PROPERTY, propertyName); + } + Property property = consumePropertyNode(edmProperty, subNode); + propertyList.add(property); + ((ObjectNode) jsonNode).remove(propertyName); + } + } + return propertyList; + } + + private Object readTypeDefinitionValue(EdmProperty edmProperty, JsonNode jsonNode) throws DeserializerException { + if (!jsonNode.isValueNode()) { + throw new DeserializerException( + "Inavlid value for property: " + edmProperty.getName() + " must not be an object or array.", + DeserializerException.MessageKeys.INVALID_VALUE_FOR_PROPERTY, edmProperty.getName()); + } + try { + EdmTypeDefinition edmTypeDefinition = (EdmTypeDefinition) edmProperty.getType(); + Object value = + edmTypeDefinition.valueOfString(jsonNode.asText(), edmProperty.isNullable(), + edmTypeDefinition.getMaxLength(), + edmTypeDefinition.getPrecision(), edmTypeDefinition.getScale(), edmTypeDefinition.isUnicode(), + edmTypeDefinition.getDefaultType()); + return value; + } catch (EdmPrimitiveTypeException e) { + throw new DeserializerException( + "Inavlid value: " + jsonNode.asText() + " for property: " + edmProperty.getName(), e, + DeserializerException.MessageKeys.INVALID_VALUE_FOR_PROPERTY, edmProperty.getName()); + } + } + + private Object readEnumValue(EdmProperty edmProperty, JsonNode jsonNode) throws DeserializerException { + if (!jsonNode.isValueNode()) { + throw new DeserializerException( + "Inavlid value for property: " + edmProperty.getName() + " must not be an object or array.", + DeserializerException.MessageKeys.INVALID_VALUE_FOR_PROPERTY, edmProperty.getName()); + } + try { + EdmEnumType edmEnumType = (EdmEnumType) edmProperty.getType(); + Object value = + edmEnumType + .valueOfString(jsonNode.asText(), edmProperty.isNullable(), edmProperty.getMaxLength(), edmProperty + .getPrecision(), edmProperty.getScale(), edmProperty.isUnicode(), edmEnumType.getDefaultType()); + return value; + } catch (EdmPrimitiveTypeException e) { + throw new DeserializerException( + "Inavlid value: " + jsonNode.asText() + " for property: " + edmProperty.getName(), e, + DeserializerException.MessageKeys.INVALID_VALUE_FOR_PROPERTY, edmProperty.getName()); + } + } + + private Object readPrimitiveValue(EdmProperty edmProperty, JsonNode jsonNode) throws DeserializerException { + if (!jsonNode.isValueNode()) { + throw new DeserializerException( + "Inavlid value for property: " + edmProperty.getName() + " must not be an object or array.", + DeserializerException.MessageKeys.INVALID_VALUE_FOR_PROPERTY, edmProperty.getName()); + } + try { + EdmPrimitiveType edmPrimitiveType = (EdmPrimitiveType) edmProperty.getType(); + Object value = + edmPrimitiveType.valueOfString(jsonNode.asText(), edmProperty.isNullable(), + edmProperty.getMaxLength(), edmProperty.getPrecision(), edmProperty.getScale(), + edmProperty.isUnicode(), edmPrimitiveType.getDefaultType()); + return value; + } catch (EdmPrimitiveTypeException e) { + throw new DeserializerException( + "Inavlid value: " + jsonNode.asText() + " for property: " + edmProperty.getName(), e, + DeserializerException.MessageKeys.INVALID_VALUE_FOR_PROPERTY, edmProperty.getName()); + } + } +} diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/deserializer/json/ODataJsonDeserializerBasicTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/deserializer/json/ODataJsonDeserializerBasicTest.java new file mode 100644 index 000000000..f3e22efca --- /dev/null +++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/deserializer/json/ODataJsonDeserializerBasicTest.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.olingo.server.core.deserializer.json; + +import static org.junit.Assert.assertNotNull; + +import org.apache.olingo.commons.api.format.ODataFormat; +import org.apache.olingo.server.api.OData; +import org.apache.olingo.server.api.deserializer.ODataDeserializer; +import org.junit.Test; + +public class ODataJsonDeserializerBasicTest { + + @Test + public void checkSupportedJsonFormats() throws Exception { + ODataDeserializer deserializer = OData.newInstance().createDeserializer(ODataFormat.JSON); + assertNotNull(deserializer); + deserializer = null; + + deserializer = OData.newInstance().createDeserializer(ODataFormat.JSON_NO_METADATA); + assertNotNull(deserializer); + deserializer = null; + + deserializer = OData.newInstance().createDeserializer(ODataFormat.JSON_FULL_METADATA); + assertNotNull(deserializer); + deserializer = null; + } +} diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/deserializer/json/AbstractODataDeserializerTest.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/deserializer/json/AbstractODataDeserializerTest.java new file mode 100644 index 000000000..185d8699c --- /dev/null +++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/deserializer/json/AbstractODataDeserializerTest.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.olingo.server.core.deserializer.json; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Collections; + +import org.apache.olingo.commons.api.edm.Edm; +import org.apache.olingo.server.api.OData; +import org.apache.olingo.server.api.edmx.EdmxReference; +import org.apache.olingo.server.tecsvc.provider.EdmTechProvider; + +public class AbstractODataDeserializerTest { + + protected static final Edm edm = OData.newInstance().createServiceMetadata( + new EdmTechProvider(), Collections. emptyList()).getEdm(); + + protected InputStream getFileAsStream(final String filename) throws IOException { + InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(filename); + if (in == null) { + throw new IOException("Requested file '" + filename + "' was not found."); + } + return in; + } +} diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/deserializer/json/ODataDeserializerDeepInsertTest.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/deserializer/json/ODataDeserializerDeepInsertTest.java new file mode 100644 index 000000000..3b9027781 --- /dev/null +++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/deserializer/json/ODataDeserializerDeepInsertTest.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.olingo.server.core.deserializer.json; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import java.io.InputStream; + +import org.apache.olingo.commons.api.data.Entity; +import org.apache.olingo.commons.api.data.Link; +import org.apache.olingo.commons.api.domain.ODataLinkType; +import org.apache.olingo.commons.api.edm.EdmEntityType; +import org.apache.olingo.commons.api.edm.FullQualifiedName; +import org.apache.olingo.commons.api.format.ODataFormat; +import org.apache.olingo.server.api.OData; +import org.junit.Test; + +public class ODataDeserializerDeepInsertTest extends AbstractODataDeserializerTest { + + @Test + public void esAllPrimExpandedToOne() throws Exception { + EdmEntityType edmEntityType = edm.getEntityType(new FullQualifiedName("Namespace1_Alias", "ETAllPrim")); + InputStream stream = getFileAsStream("EntityESAllPrimExpandedNavPropertyETTwoPrimOne.json"); + Entity entity = OData.newInstance().createDeserializer(ODataFormat.JSON).entity(stream, edmEntityType); + + Link navigationLink = entity.getNavigationLink("NavPropertyETTwoPrimOne"); + assertNotNull(navigationLink); + + assertEquals("NavPropertyETTwoPrimOne", navigationLink.getTitle()); + assertEquals(ODataLinkType.ENTITY_NAVIGATION.toString(), navigationLink.getType()); + assertNotNull(navigationLink.getInlineEntity()); + assertNull(navigationLink.getInlineEntitySet()); + } + + @Test + public void esAllPrimExpandedToMany() throws Exception { + EdmEntityType edmEntityType = edm.getEntityType(new FullQualifiedName("Namespace1_Alias", "ETAllPrim")); + InputStream stream = getFileAsStream("EntityESAllPrimExpandedNavPropertyETTwoPrimMany.json"); + Entity entity = OData.newInstance().createDeserializer(ODataFormat.JSON).entity(stream, edmEntityType); + + Link navigationLink = entity.getNavigationLink("NavPropertyETTwoPrimMany"); + assertNotNull(navigationLink); + + assertEquals("NavPropertyETTwoPrimMany", navigationLink.getTitle()); + assertEquals(ODataLinkType.ENTITY_SET_NAVIGATION.toString(), navigationLink.getType()); + assertNull(navigationLink.getInlineEntity()); + assertNotNull(navigationLink.getInlineEntitySet()); + assertEquals(1, navigationLink.getInlineEntitySet().getEntities().size()); + } + +} diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/deserializer/json/ODataDeserializerEntitySetTest.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/deserializer/json/ODataDeserializerEntitySetTest.java new file mode 100644 index 000000000..9cefff699 --- /dev/null +++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/deserializer/json/ODataDeserializerEntitySetTest.java @@ -0,0 +1,83 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.olingo.server.core.deserializer.json; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import java.io.InputStream; +import java.math.BigDecimal; +import java.util.List; + +import org.apache.olingo.commons.api.data.Entity; +import org.apache.olingo.commons.api.data.EntitySet; +import org.apache.olingo.commons.api.data.Property; +import org.apache.olingo.commons.api.edm.EdmEntityType; +import org.apache.olingo.commons.api.edm.FullQualifiedName; +import org.apache.olingo.commons.api.format.ODataFormat; +import org.apache.olingo.server.api.OData; +import org.junit.Test; + +public class ODataDeserializerEntitySetTest extends AbstractODataDeserializerTest { + + @Test + public void esAllPrim() throws Exception { + EdmEntityType edmEntityType = edm.getEntityType(new FullQualifiedName("Namespace1_Alias", "ETAllPrim")); + InputStream stream = getFileAsStream("ESAllPrim.json"); + EntitySet entitySet = OData.newInstance().createDeserializer(ODataFormat.JSON).entitySet(stream, edmEntityType); + + assertNotNull(entitySet); + assertEquals(3, entitySet.getEntities().size()); + + //Check first entity + Entity entity = entitySet.getEntities().get(0); + List properties = entity.getProperties(); + assertNotNull(properties); + assertEquals(16, properties.size()); + + assertEquals(new Short((short) 32767), entity.getProperty("PropertyInt16").getValue()); + assertEquals("First Resource - positive values", entity.getProperty("PropertyString").getValue()); + assertEquals(new Boolean(true), entity.getProperty("PropertyBoolean").getValue()); + assertEquals(new Short((short) 255), entity.getProperty("PropertyByte").getValue()); + assertEquals(new Byte((byte) 127), entity.getProperty("PropertySByte").getValue()); + assertEquals(new Integer(2147483647), entity.getProperty("PropertyInt32").getValue()); + assertEquals(new Long(9223372036854775807l), entity.getProperty("PropertyInt64").getValue()); + assertEquals(new Float(1.79E20), entity.getProperty("PropertySingle").getValue()); + assertEquals(new Double(-1.79E19), entity.getProperty("PropertyDouble").getValue()); + assertEquals(new BigDecimal(34), entity.getProperty("PropertyDecimal").getValue()); + assertNotNull(entity.getProperty("PropertyBinary").getValue()); + assertNotNull(entity.getProperty("PropertyDate").getValue()); + assertNotNull(entity.getProperty("PropertyDateTimeOffset").getValue()); + assertNotNull(entity.getProperty("PropertyDuration").getValue()); + assertNotNull(entity.getProperty("PropertyGuid").getValue()); + assertNotNull(entity.getProperty("PropertyTimeOfDay").getValue()); + } + + @Test + public void eSCompCollComp() throws Exception { + EdmEntityType edmEntityType = edm.getEntityType(new FullQualifiedName("Namespace1_Alias", "ETCompCollComp")); + InputStream stream = getFileAsStream("ESCompCollComp.json"); + EntitySet entitySet = OData.newInstance().createDeserializer(ODataFormat.JSON).entitySet(stream, edmEntityType); + + assertNotNull(entitySet); + assertEquals(2, entitySet.getEntities().size()); + + //Since entity deserialization is called we do not check all entities here excplicitly + } +} diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/deserializer/json/ODataJsonDeserializerEntityTest.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/deserializer/json/ODataJsonDeserializerEntityTest.java new file mode 100644 index 000000000..c9aad1a36 --- /dev/null +++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/deserializer/json/ODataJsonDeserializerEntityTest.java @@ -0,0 +1,236 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.olingo.server.core.deserializer.json; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.math.BigDecimal; +import java.util.List; + +import org.apache.olingo.commons.api.data.Entity; +import org.apache.olingo.commons.api.data.Property; +import org.apache.olingo.commons.api.data.ValueType; +import org.apache.olingo.commons.api.edm.FullQualifiedName; +import org.apache.olingo.commons.api.format.ODataFormat; +import org.apache.olingo.server.api.OData; +import org.apache.olingo.server.api.deserializer.ODataDeserializer; +import org.junit.Test; + +public class ODataJsonDeserializerEntityTest extends AbstractODataDeserializerTest { + + @Test + public void simpleEntityETAllPrim() throws Exception { + String entityString = + "{\"PropertyInt16\":32767," + + "\"PropertyString\":\"First Resource - positive values\"," + + "\"PropertyBoolean\":true," + + "\"PropertyByte\":255," + + "\"PropertySByte\":127," + + "\"PropertyInt32\":2147483647," + + "\"PropertyInt64\":9223372036854775807," + + "\"PropertySingle\":1.79E20," + + "\"PropertyDouble\":-1.79E19," + + "\"PropertyDecimal\":34," + + "\"PropertyBinary\":\"ASNFZ4mrze8=\"," + + "\"PropertyDate\":\"2012-12-03\"," + + "\"PropertyDateTimeOffset\":\"2012-12-03T07:16:23Z\"," + + "\"PropertyDuration\":\"PT6S\"," + + "\"PropertyGuid\":\"01234567-89ab-cdef-0123-456789abcdef\"," + + "\"PropertyTimeOfDay\":\"03:26:05\"}"; + InputStream stream = new ByteArrayInputStream(entityString.getBytes()); + ODataDeserializer deserializer = OData.newInstance().createDeserializer(ODataFormat.JSON); + Entity entity = + deserializer.entity(stream, edm.getEntityType(new FullQualifiedName("Namespace1_Alias", "ETAllPrim"))); + assertNotNull(entity); + List properties = entity.getProperties(); + assertNotNull(properties); + assertEquals(16, properties.size()); + + assertEquals(new Short((short) 32767), entity.getProperty("PropertyInt16").getValue()); + assertEquals("First Resource - positive values", entity.getProperty("PropertyString").getValue()); + assertEquals(new Boolean(true), entity.getProperty("PropertyBoolean").getValue()); + assertEquals(new Short((short) 255), entity.getProperty("PropertyByte").getValue()); + assertEquals(new Byte((byte) 127), entity.getProperty("PropertySByte").getValue()); + assertEquals(new Integer(2147483647), entity.getProperty("PropertyInt32").getValue()); + assertEquals(new Long(9223372036854775807l), entity.getProperty("PropertyInt64").getValue()); + assertEquals(new Float(1.79E20), entity.getProperty("PropertySingle").getValue()); + assertEquals(new Double(-1.79E19), entity.getProperty("PropertyDouble").getValue()); + assertEquals(new BigDecimal(34), entity.getProperty("PropertyDecimal").getValue()); + assertNotNull(entity.getProperty("PropertyBinary").getValue()); + assertNotNull(entity.getProperty("PropertyDate").getValue()); + assertNotNull(entity.getProperty("PropertyDateTimeOffset").getValue()); + assertNotNull(entity.getProperty("PropertyDuration").getValue()); + assertNotNull(entity.getProperty("PropertyGuid").getValue()); + assertNotNull(entity.getProperty("PropertyTimeOfDay").getValue()); + } + + @Test + public void simpleEntityETCompAllPrim() throws Exception { + String entityString = "{\"PropertyInt16\":32767," + + "\"PropertyComp\":{" + + "\"PropertyString\":\"First Resource - first\"," + + "\"PropertyBinary\":\"ASNFZ4mrze8=\"," + + "\"PropertyBoolean\":true," + + "\"PropertyByte\":255," + + "\"PropertyDate\":\"2012-10-03\"," + + "\"PropertyDateTimeOffset\":\"2012-10-03T07:16:23.1234567Z\"," + + "\"PropertyDecimal\":34.27," + + "\"PropertySingle\":1.79E20," + + "\"PropertyDouble\":-1.79E19," + + "\"PropertyDuration\":\"PT6S\"," + + "\"PropertyGuid\":\"01234567-89ab-cdef-0123-456789abcdef\"," + + "\"PropertyInt16\":32767," + + "\"PropertyInt32\":2147483647," + + "\"PropertyInt64\":9223372036854775807," + + "\"PropertySByte\":127," + + "\"PropertyTimeOfDay\":\"01:00:01\"}}"; + InputStream stream = new ByteArrayInputStream(entityString.getBytes()); + ODataDeserializer deserializer = OData.newInstance().createDeserializer(ODataFormat.JSON); + Entity entity = + deserializer.entity(stream, edm.getEntityType(new FullQualifiedName("Namespace1_Alias", "ETCompAllPrim"))); + assertNotNull(entity); + List properties = entity.getProperties(); + assertNotNull(properties); + assertEquals(2, properties.size()); + + assertEquals(new Short((short) 32767), entity.getProperty("PropertyInt16").getValue()); + + assertNotNull(entity.getProperty("PropertyComp")); + assertNotNull(entity.getProperty("PropertyComp") instanceof List); + List complexProperties = entity.getProperty("PropertyComp").asComplex(); + assertEquals(16, complexProperties.size()); + } + + @Test + public void simpleEntityETCollAllPrim() throws Exception { + final String entityString = "{" + + "\"PropertyInt16\":1," + + "\"CollPropertyString\":" + + "[\"Employee1@company.example\",\"Employee2@company.example\",\"Employee3@company.example\"]," + + "\"CollPropertyBoolean\":[true,false,true]," + + "\"CollPropertyByte\":[50,200,249]," + + "\"CollPropertySByte\":[-120,120,126]," + + "\"CollPropertyInt16\":[1000,2000,30112]," + + "\"CollPropertyInt32\":[23232323,11223355,10000001]," + + "\"CollPropertyInt64\":[929292929292,333333333333,444444444444]," + + "\"CollPropertySingle\":[1790.0,26600.0,3210.0]," + + "\"CollPropertyDouble\":[-17900.0,-2.78E7,3210.0]," + + "\"CollPropertyDecimal\":[12,-2,1234]," + + "\"CollPropertyBinary\":[\"q83v\",\"ASNF\",\"VGeJ\"]," + + "\"CollPropertyDate\":[\"1958-12-03\",\"1999-08-05\",\"2013-06-25\"]," + + "\"CollPropertyDateTimeOffset\":[\"2015-08-12T03:08:34Z\",\"1970-03-28T12:11:10Z\"," + + "\"1948-02-17T09:09:09Z\"]," + + "\"CollPropertyDuration\":[\"PT13S\",\"PT5H28M0S\",\"PT1H0S\"]," + + "\"CollPropertyGuid\":[\"ffffff67-89ab-cdef-0123-456789aaaaaa\",\"eeeeee67-89ab-cdef-0123-456789bbbbbb\"," + + "\"cccccc67-89ab-cdef-0123-456789cccccc\"]," + + "\"CollPropertyTimeOfDay\":[\"04:14:13\",\"23:59:59\",\"01:12:33\"]" + + "}"; + InputStream stream = new ByteArrayInputStream(entityString.getBytes()); + ODataDeserializer deserializer = OData.newInstance().createDeserializer(ODataFormat.JSON); + Entity entity = + deserializer.entity(stream, edm.getEntityType(new FullQualifiedName("Namespace1_Alias", "ETCollAllPrim"))); + assertNotNull(entity); + List properties = entity.getProperties(); + assertNotNull(properties); + assertEquals(17, properties.size()); + + // All properties need 3 entires + for (Property prop : properties) { + if (!prop.getName().equals("PropertyInt16")) { + assertEquals(ValueType.COLLECTION_PRIMITIVE, prop.getValueType()); + assertTrue(prop.getValue() instanceof List); + List asCollection = prop.asCollection(); + assertEquals(3, asCollection.size()); + } + } + Property property = entity.getProperty("CollPropertyBoolean"); + List asCollection = property.asCollection(); + assertEquals(true, asCollection.get(0)); + assertEquals(false, asCollection.get(1)); + assertEquals(true, asCollection.get(2)); + } + + @SuppressWarnings("unchecked") + @Test + public void simpleEntityETMixPrimCollComp() throws Exception { + final String entityString = "{" + + "\"PropertyInt16\":32767," + + "\"CollPropertyString\":" + + "[\"Employee1@company.example\",\"Employee2@company.example\",\"Employee3@company.example\"]," + + "\"PropertyComp\":{\"PropertyInt16\":111,\"PropertyString\":\"TEST A\"}," + + "\"CollPropertyComp\":[" + + "{\"PropertyInt16\":123,\"PropertyString\":\"TEST 1\"}," + + "{\"PropertyInt16\":456,\"PropertyString\":\"TEST 2\"}," + + "{\"PropertyInt16\":789,\"PropertyString\":\"TEST 3\"}]}"; + + InputStream stream = new ByteArrayInputStream(entityString.getBytes()); + ODataDeserializer deserializer = OData.newInstance().createDeserializer(ODataFormat.JSON); + Entity entity = + deserializer.entity(stream, edm.getEntityType(new FullQualifiedName("Namespace1_Alias", "ETMixPrimCollComp"))); + assertNotNull(entity); + List properties = entity.getProperties(); + assertNotNull(properties); + assertEquals(4, properties.size()); + + Property property = entity.getProperty("CollPropertyComp"); + assertEquals(ValueType.COLLECTION_COMPLEX, property.getValueType()); + + assertTrue(property.getValue() instanceof List); + List asCollection = property.asCollection(); + assertEquals(3, asCollection.size()); + + for (Object arrayElement : asCollection) { + assertTrue(arrayElement instanceof List); + List castedArrayElement = (List) arrayElement; + assertEquals(2, castedArrayElement.size()); + } + } + + @Test + public void simpleEntityWithContextURL() throws Exception { + String entityString = + "{\"@odata.context\": \"$metadata#ESAllPrim/$entity\"," + + "\"PropertyInt16\":32767," + + "\"PropertyString\":\"First Resource - positive values\"," + + "\"PropertyBoolean\":true," + + "\"PropertyByte\":255," + + "\"PropertySByte\":127," + + "\"PropertyInt32\":2147483647," + + "\"PropertyInt64\":9223372036854775807," + + "\"PropertySingle\":1.79E20," + + "\"PropertyDouble\":-1.79E19," + + "\"PropertyDecimal\":34," + + "\"PropertyBinary\":\"ASNFZ4mrze8=\"," + + "\"PropertyDate\":\"2012-12-03\"," + + "\"PropertyDateTimeOffset\":\"2012-12-03T07:16:23Z\"," + + "\"PropertyDuration\":\"PT6S\"," + + "\"PropertyGuid\":\"01234567-89ab-cdef-0123-456789abcdef\"," + + "\"PropertyTimeOfDay\":\"03:26:05\"}"; + InputStream stream = new ByteArrayInputStream(entityString.getBytes()); + ODataDeserializer deserializer = OData.newInstance().createDeserializer(ODataFormat.JSON); + Entity entity = + deserializer.entity(stream, edm.getEntityType(new FullQualifiedName("Namespace1_Alias", "ETAllPrim"))); + assertNotNull(entity); + } + +} diff --git a/lib/server-test/src/test/resources/ESAllPrim.json b/lib/server-test/src/test/resources/ESAllPrim.json new file mode 100644 index 000000000..75a6a2249 --- /dev/null +++ b/lib/server-test/src/test/resources/ESAllPrim.json @@ -0,0 +1,56 @@ +{ + "@odata.context" : "$metadata#ESAllPrim", + "value" : [{ + "PropertyInt16" : 32767, + "PropertyString" : "First Resource - positive values", + "PropertyBoolean" : true, + "PropertyByte" : 255, + "PropertySByte" : 127, + "PropertyInt32" : 2147483647, + "PropertyInt64" : 9223372036854775807, + "PropertySingle" : 1.79000000E+20, + "PropertyDouble" : -1.7900000000000000E+19, + "PropertyDecimal" : 34, + "PropertyBinary" : "ASNFZ4mrze8=", + "PropertyDate" : "2012-12-03", + "PropertyDateTimeOffset" : "2012-12-03T07:16:23Z", + "PropertyDuration" : "P0DT0H0M6S", + "PropertyGuid" : "01234567-89ab-cdef-0123-456789abcdef", + "PropertyTimeOfDay" : "03:26:05" + }, { + "PropertyInt16" : -32768, + "PropertyString" : "Second Resource - negative values", + "PropertyBoolean" : false, + "PropertyByte" : 0, + "PropertySByte" : -128, + "PropertyInt32" : -2147483648, + "PropertyInt64" : -9223372036854775808, + "PropertySingle" : -1.79000000E+08, + "PropertyDouble" : -1.7900000000000000E+05, + "PropertyDecimal" : -34, + "PropertyBinary" : "ASNFZ4mrze8=", + "PropertyDate" : "2015-11-05", + "PropertyDateTimeOffset" : "2005-12-03T07:17:08Z", + "PropertyDuration" : "P0DT0H0M9S", + "PropertyGuid" : "76543201-23ab-cdef-0123-456789dddfff", + "PropertyTimeOfDay" : "23:49:14" + }, { + "PropertyInt16" : 0, + "PropertyString" : "", + "PropertyBoolean" : false, + "PropertyByte" : 0, + "PropertySByte" : 0, + "PropertyInt32" : 0, + "PropertyInt64" : 0, + "PropertySingle" : 0.00000000E+00, + "PropertyDouble" : 0.0000000000000000E+00, + "PropertyDecimal" : 0, + "PropertyBinary" : "", + "PropertyDate" : "1970-01-01", + "PropertyDateTimeOffset" : "2005-12-03T00:00:00Z", + "PropertyDuration" : "P0DT0H0M0S", + "PropertyGuid" : "76543201-23ab-cdef-0123-456789cccddd", + "PropertyTimeOfDay" : "00:01:01" + } + ] +} diff --git a/lib/server-test/src/test/resources/ESCompCollComp.json b/lib/server-test/src/test/resources/ESCompCollComp.json new file mode 100644 index 000000000..fa0233822 --- /dev/null +++ b/lib/server-test/src/test/resources/ESCompCollComp.json @@ -0,0 +1,35 @@ +{ + "@odata.context" : "$metadata#ESCompCollComp", + "value" : [{ + "PropertyInt16" : 32767, + "PropertyComp" : { + "CollPropertyComp" : [{ + "PropertyInt16" : 555, + "PropertyString" : "1 Test Complex in Complex Property" + }, { + "PropertyInt16" : 666, + "PropertyString" : "2 Test Complex in Complex property" + }, { + "PropertyInt16" : 777, + "PropertyString" : "3 Test Complex in Complex property" + } + ] + } + }, { + "PropertyInt16" : 12345, + "PropertyComp" : { + "CollPropertyComp" : [{ + "PropertyInt16" : 888, + "PropertyString" : "11 Test Complex in Complex property" + }, { + "PropertyInt16" : 999, + "PropertyString" : "12 Test Complex in Complex property" + }, { + "PropertyInt16" : 0, + "PropertyString" : "13 Test Complex in Complex property" + } + ] + } + } + ] +} diff --git a/lib/server-test/src/test/resources/EntityESAllPrimExpandedNavPropertyETTwoPrimMany.json b/lib/server-test/src/test/resources/EntityESAllPrimExpandedNavPropertyETTwoPrimMany.json new file mode 100644 index 000000000..b15788fb4 --- /dev/null +++ b/lib/server-test/src/test/resources/EntityESAllPrimExpandedNavPropertyETTwoPrimMany.json @@ -0,0 +1,24 @@ +{ + "@odata.context" : "$metadata#ESAllPrim/$entity", + "PropertyInt16" : 32767, + "PropertyString" : "First Resource - positive values", + "PropertyBoolean" : true, + "PropertyByte" : 255, + "PropertySByte" : 127, + "PropertyInt32" : 2147483647, + "PropertyInt64" : 9223372036854775807, + "PropertySingle" : 1.79000000E+20, + "PropertyDouble" : -1.7900000000000000E+19, + "PropertyDecimal" : 34, + "PropertyBinary" : "ASNFZ4mrze8=", + "PropertyDate" : "2012-12-03", + "PropertyDateTimeOffset" : "2012-12-03T07:16:23Z", + "PropertyDuration" : "P0DT0H0M6S", + "PropertyGuid" : "01234567-89ab-cdef-0123-456789abcdef", + "PropertyTimeOfDay" : "03:26:05", + "NavPropertyETTwoPrimMany" : [{ + "PropertyInt16" : -365, + "PropertyString" : "Test String2" + } + ] +} diff --git a/lib/server-test/src/test/resources/EntityESAllPrimExpandedNavPropertyETTwoPrimOne.json b/lib/server-test/src/test/resources/EntityESAllPrimExpandedNavPropertyETTwoPrimOne.json new file mode 100644 index 000000000..a68a55498 --- /dev/null +++ b/lib/server-test/src/test/resources/EntityESAllPrimExpandedNavPropertyETTwoPrimOne.json @@ -0,0 +1,23 @@ +{ + "@odata.context" : "$metadata#ESAllPrim/$entity", + "PropertyInt16" : 32767, + "PropertyString" : "First Resource - positive values", + "PropertyBoolean" : true, + "PropertyByte" : 255, + "PropertySByte" : 127, + "PropertyInt32" : 2147483647, + "PropertyInt64" : 9223372036854775807, + "PropertySingle" : 1.79000000E+20, + "PropertyDouble" : -1.7900000000000000E+19, + "PropertyDecimal" : 34, + "PropertyBinary" : "ASNFZ4mrze8=", + "PropertyDate" : "2012-12-03", + "PropertyDateTimeOffset" : "2012-12-03T07:16:23Z", + "PropertyDuration" : "P0DT0H0M6S", + "PropertyGuid" : "01234567-89ab-cdef-0123-456789abcdef", + "PropertyTimeOfDay" : "03:26:05", + "NavPropertyETTwoPrimOne" : { + "PropertyInt16" : 32767, + "PropertyString" : "Test String4" + } +}