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 index d30d97352..9106613a0 100644 --- 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 @@ -20,9 +20,12 @@ package org.apache.olingo.server.core.deserializer.json; import java.io.IOException; import java.io.InputStream; +import java.math.BigDecimal; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; @@ -53,6 +56,14 @@ import org.apache.olingo.commons.api.edm.EdmType; import org.apache.olingo.commons.api.edm.EdmTypeDefinition; import org.apache.olingo.commons.api.edm.FullQualifiedName; import org.apache.olingo.commons.api.edm.constants.EdmTypeKind; +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.format.ContentType; import org.apache.olingo.server.api.ServiceMetadata; import org.apache.olingo.server.api.deserializer.DeserializerException; @@ -76,6 +87,19 @@ import com.fasterxml.jackson.databind.node.ObjectNode; public class ODataJsonDeserializer implements ODataDeserializer { + private static final Map> jsonNameToGeoDataType; + static { + Map> temp = new HashMap>(); + temp.put(Constants.ELEM_POINT, Point.class); + temp.put(Constants.ELEM_MULTIPOINT, MultiPoint.class); + temp.put(Constants.ELEM_LINESTRING, LineString.class); + temp.put("MultiLineString", MultiLineString.class); + temp.put(Constants.ELEM_POLYGON, Polygon.class); + temp.put("MultiPolygon", MultiPolygon.class); + temp.put("GeometryCollection", GeospatialCollection.class); + jsonNameToGeoDataType = Collections.unmodifiableMap(temp); + } + private static final String ODATA_ANNOTATION_MARKER = "@"; private static final String ODATA_CONTROL_INFORMATION_PREFIX = "@odata."; @@ -594,16 +618,21 @@ public class ODataJsonDeserializer implements ODataDeserializer { private Object readPrimitiveValue(final String name, final EdmPrimitiveType type, final boolean isNullable, final Integer maxLength, final Integer precision, final Integer scale, final boolean isUnicode, final EdmMapping mapping, final JsonNode jsonNode) throws DeserializerException { - checkForValueNode(name, jsonNode); if (isValidNull(name, isNullable, jsonNode)) { return null; } + final boolean isGeoType = type.getName().startsWith("Geo"); + if (!isGeoType) { + checkForValueNode(name, jsonNode); + } checkJsonTypeBasedOnPrimitiveType(name, type, jsonNode); - Class javaClass = getJavaClassForPrimitiveType(mapping, type); try { + if (isGeoType) { + return readPrimitiveGeoValue(name, type, (ObjectNode) jsonNode); + } return type.valueOfString(jsonNode.asText(), isNullable, maxLength, precision, scale, isUnicode, - javaClass); + getJavaClassForPrimitiveType(mapping, type)); } catch (final EdmPrimitiveTypeException e) { throw new DeserializerException( "Invalid value: " + jsonNode.asText() + " for property: " + name, e, @@ -624,6 +653,132 @@ public class ODataJsonDeserializer implements ODataDeserializer { return false; } + /** + * Reads a geospatial JSON value following the GeoJSON specification defined in RFC 7946. + * @param name property name + * @param type EDM type of the value + * (can be null for recursive calls while parsing a GeometryCollection) + */ + private Geospatial readPrimitiveGeoValue(final String name, final EdmPrimitiveType type, ObjectNode jsonNode) + throws DeserializerException, EdmPrimitiveTypeException { + JsonNode typeNode = jsonNode.remove(Constants.ATTR_TYPE); + if (typeNode != null && typeNode.isTextual()) { + final Class geoDataType = jsonNameToGeoDataType.get(typeNode.asText()); + if (geoDataType != null && (type == null || geoDataType.equals(type.getDefaultType()))) { + final JsonNode topNode = jsonNode.remove( + geoDataType.equals(GeospatialCollection.class) ? Constants.JSON_GEOMETRIES : Constants.JSON_COORDINATES); + + // The "crs" member mentioned in some versions of the OData specification is not part of GeoJSON. + // It used to be used to specify the coordinate reference system. + // TODO: Is it OK to follow RFC 7946 strictly and not allow this element from its obsolete predecessor? + assertJsonNodeIsEmpty(jsonNode); + + if (topNode != null && topNode.isArray()) { + final Geospatial.Dimension dimension = type == null || type.getName().startsWith("Geometry") ? + Geospatial.Dimension.GEOMETRY : + Geospatial.Dimension.GEOGRAPHY; + if (geoDataType.equals(Point.class)) { + return readGeoPointValue(name, dimension, topNode); + } else if (geoDataType.equals(MultiPoint.class)) { + return new MultiPoint(dimension, null, readGeoPointValues(name, dimension, 0, false, topNode)); + } else if (geoDataType.equals(LineString.class)) { + // Although a line string with less than two points is not really one, the OData specification says: + // "The coordinates member of a LineString can have zero or more positions". + // Therefore the required minimal size of the points array currently is zero. + return new LineString(dimension, null, readGeoPointValues(name, dimension, 0, false, topNode)); + } else if (geoDataType.equals(MultiLineString.class)) { + List lines = new ArrayList(); + for (final JsonNode element : topNode) { + // Line strings can be empty (see above). + lines.add(new LineString(dimension, null, readGeoPointValues(name, dimension, 0, false, element))); + } + return new MultiLineString(dimension, null, lines); + } else if (geoDataType.equals(Polygon.class)) { + return readGeoPolygon(name, dimension, topNode); + } else if (geoDataType.equals(MultiPolygon.class)) { + List polygons = new ArrayList(); + for (final JsonNode element : topNode) { + polygons.add(readGeoPolygon(name, dimension, element)); + } + return new MultiPolygon(dimension, null, polygons); + } else if (geoDataType.equals(GeospatialCollection.class)) { + List elements = new ArrayList(); + for (final JsonNode element : topNode) { + if (element.isObject()) { + elements.add(readPrimitiveGeoValue(name, null, (ObjectNode) element)); + } else { + throw new DeserializerException("Invalid value '" + element + "' in property: " + name, + DeserializerException.MessageKeys.INVALID_VALUE_FOR_PROPERTY, name); + } + } + return new GeospatialCollection(dimension, null, elements); + } + } + } + } + throw new DeserializerException("Invalid value '" + jsonNode + "' for property: " + name, + DeserializerException.MessageKeys.INVALID_VALUE_FOR_PROPERTY, name); + } + + private Point readGeoPointValue(final String name, final Geospatial.Dimension dimension, JsonNode node) + throws DeserializerException, EdmPrimitiveTypeException { + if (node.isArray() && (node.size() ==2 || node.size() == 3) + && node.get(0).isNumber() && node.get(1).isNumber() && (node.get(2) == null || node.get(2).isNumber())) { + Point point = new Point(dimension, null); + point.setX(getDoubleValue(node.get(0).asText())); + point.setY(getDoubleValue(node.get(1).asText())); + if (node.get(2) != null) { + point.setZ(getDoubleValue(node.get(2).asText())); + } + return point; + } + throw new DeserializerException("Invalid point value '" + node + "' in property: " + name, + DeserializerException.MessageKeys.INVALID_VALUE_FOR_PROPERTY, name); + } + + private double getDoubleValue(final String value) throws EdmPrimitiveTypeException { + final BigDecimal bigDecimalValue = new BigDecimal(value); + final Double result = bigDecimalValue.doubleValue(); + // "Real" infinite values cannot occur, so we can throw an exception + // if the conversion to a double results in an infinite value. + // An exception is also thrown if the number cannot be stored in a double without loss. + if (result.isInfinite() || BigDecimal.valueOf(result).compareTo(bigDecimalValue) != 0) { + throw new EdmPrimitiveTypeException("The literal '" + value + "' has illegal content."); + } + return result; + } + + private List readGeoPointValues(final String name, final Geospatial.Dimension dimension, + final int minimalSize, final boolean closed, JsonNode node) + throws DeserializerException, EdmPrimitiveTypeException { + if (node.isArray()) { + List points = new ArrayList(); + for (final JsonNode element : node) { + points.add(readGeoPointValue(name, dimension, element)); + } + if (points.size() >= minimalSize + && (!closed || points.get(points.size() - 1).equals(points.get(0)))) { + return points; + } + } + throw new DeserializerException("Invalid point values '" + node + "' in property: " + name, + DeserializerException.MessageKeys.INVALID_VALUE_FOR_PROPERTY, name); + } + + private Polygon readGeoPolygon(final String name, final Geospatial.Dimension dimension, JsonNode node) + throws DeserializerException, EdmPrimitiveTypeException { + // GeoJSON would allow for more than one interior polygon (hole). + // But there is no place in the data object to store this information so for now we throw an error. + // There could be a more strict verification that the lines describe boundaries and have the correct winding order. + if (node.isArray() && (node.size() == 1 || node.size() == 2)) { + return new Polygon(dimension, null, + node.size() > 1 ? readGeoPointValues(name, dimension, 4, true, node.get(1)) : null, + readGeoPointValues(name, dimension, 4, true, node.get(0))); + } + throw new DeserializerException("Invalid polygon values '" + node + "' in property: " + name, + DeserializerException.MessageKeys.INVALID_VALUE_FOR_PROPERTY, name); + } + /** * Returns the primitive type's default class or the manually mapped class if present. * @param mapping @@ -704,7 +859,8 @@ public class ODataJsonDeserializer implements ODataDeserializer { valid = matchTextualCase(jsonNode, primKind) || matchNumberCase(jsonNode, primKind) || matchBooleanCase(jsonNode, primKind) - || matchIEEENumberCase(jsonNode, primKind); + || matchIEEENumberCase(jsonNode, primKind) + || jsonNode.isObject() && name.startsWith("Geo"); } if (!valid) { throw new DeserializerException( @@ -837,17 +993,11 @@ public class ODataJsonDeserializer implements ODataDeserializer { DeserializerException.MessageKeys.UNKNOWN_CONTENT); } - EdmStructuredType currentEdmType = null; - if (edmType instanceof EdmEntityType) { - currentEdmType = serviceMetadata.getEdm() - .getEntityType(new FullQualifiedName(odataType)); - } else { - currentEdmType = serviceMetadata.getEdm() - .getComplexType(new FullQualifiedName(odataType)); - } + final EdmStructuredType currentEdmType = edmType.getKind() == EdmTypeKind.ENTITY ? + serviceMetadata.getEdm().getEntityType(new FullQualifiedName(odataType)) : + serviceMetadata.getEdm().getComplexType(new FullQualifiedName(odataType)); if (!isAssignable(edmType, currentEdmType)) { - throw new DeserializerException( - "Odata type " + odataType + " not allowed here", + throw new DeserializerException("Odata type " + odataType + " not allowed here", DeserializerException.MessageKeys.UNKNOWN_CONTENT); } @@ -859,14 +1009,8 @@ public class ODataJsonDeserializer implements ODataDeserializer { private boolean isAssignable(final EdmStructuredType edmStructuredType, final EdmStructuredType edmStructuredTypeToAssign) { - if (edmStructuredTypeToAssign == null) { - return false; - } else if (edmStructuredType.getFullQualifiedName() - .equals(edmStructuredTypeToAssign.getFullQualifiedName())) { - return true; - } else { - return isAssignable(edmStructuredType, - edmStructuredTypeToAssign.getBaseType()); - } + return edmStructuredTypeToAssign != null + && (edmStructuredType.getFullQualifiedName().equals(edmStructuredTypeToAssign.getFullQualifiedName()) + || isAssignable(edmStructuredType, edmStructuredTypeToAssign.getBaseType())); } } diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java index 57af7a95d..a86bdf027 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java @@ -21,8 +21,10 @@ package org.apache.olingo.server.core.serializer.json; import java.io.IOException; import java.io.OutputStream; import java.util.Collections; +import java.util.EnumMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import org.apache.olingo.commons.api.Constants; @@ -47,6 +49,15 @@ import org.apache.olingo.commons.api.edm.EdmStructuredType; import org.apache.olingo.commons.api.edm.EdmType; import org.apache.olingo.commons.api.edm.FullQualifiedName; import org.apache.olingo.commons.api.edm.constants.EdmTypeKind; +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.format.ContentType; import org.apache.olingo.commons.core.edm.primitivetype.EdmPrimitiveTypeFactory; import org.apache.olingo.server.api.ODataServerError; @@ -81,6 +92,19 @@ import com.fasterxml.jackson.core.JsonGenerator; public class ODataJsonSerializer extends AbstractODataSerializer { + private static final Map geoValueTypeToJsonName; + static { + Map temp = new EnumMap(Geospatial.Type.class); + temp.put(Geospatial.Type.POINT, Constants.ELEM_POINT); + temp.put(Geospatial.Type.MULTIPOINT, Constants.ELEM_MULTIPOINT); + temp.put(Geospatial.Type.LINESTRING, Constants.ELEM_LINESTRING); + temp.put(Geospatial.Type.MULTILINESTRING, "MultiLineString"); + temp.put(Geospatial.Type.POLYGON, Constants.ELEM_POLYGON); + temp.put(Geospatial.Type.MULTIPOLYGON, "MultiPolygon"); + temp.put(Geospatial.Type.GEOSPATIALCOLLECTION, "GeometryCollection"); + geoValueTypeToJsonName = Collections.unmodifiableMap(temp); + } + private final boolean isIEEE754Compatible; private final boolean isODataMetadataNone; private final boolean isODataMetadataFull; @@ -701,6 +725,7 @@ public class ODataJsonSerializer extends AbstractODataSerializer { switch (property.getValueType()) { case COLLECTION_PRIMITIVE: case COLLECTION_ENUM: + case COLLECTION_GEOSPATIAL: try { writePrimitiveValue(property.getName(), type, value, isNullable, maxLength, precision, scale, isUnicode, json); @@ -710,9 +735,6 @@ public class ODataJsonSerializer extends AbstractODataSerializer { property.getName(), property.getValue().toString()); } break; - case COLLECTION_GEOSPATIAL: - throw new SerializerException("Property type not yet supported!", - SerializerException.MessageKeys.UNSUPPORTED_PROPERTY_TYPE, property.getName()); default: throw new SerializerException("Property type not yet supported!", SerializerException.MessageKeys.UNSUPPORTED_PROPERTY_TYPE, property.getName()); @@ -753,8 +775,7 @@ public class ODataJsonSerializer extends AbstractODataSerializer { writePrimitiveValue(property.getName(), type, property.asPrimitive(), isNullable, maxLength, precision, scale, isUnicode, json); } else if (property.isGeospatial()) { - throw new SerializerException("Property type not yet supported!", - SerializerException.MessageKeys.UNSUPPORTED_PROPERTY_TYPE, property.getName()); + writeGeoValue(property.getName(), type, property.asGeospatial(), isNullable, json); } else if (property.isEnum()) { writePrimitiveValue(property.getName(), type, property.asEnum(), isNullable, maxLength, precision, scale, isUnicode, json); @@ -808,12 +829,105 @@ public class ODataJsonSerializer extends AbstractODataSerializer { } } + /** Writes a geospatial value following the GeoJSON specification defined in RFC 7946. */ + protected void writeGeoValue(final String name, final EdmPrimitiveType type, final Geospatial geoValue, + final Boolean isNullable, JsonGenerator json) + throws EdmPrimitiveTypeException, IOException, SerializerException { + if (geoValue == null) { + if (isNullable == null || isNullable) { + json.writeNull(); + } else { + throw new EdmPrimitiveTypeException("The literal 'null' is not allowed."); + } + } else { + if (!type.getDefaultType().isAssignableFrom(geoValue.getClass())) { + throw new EdmPrimitiveTypeException("The value type " + geoValue.getClass() + " is not supported."); + } + if (geoValue.getSrid() != null && geoValue.getSrid().isNotDefault()) { + throw new SerializerException("Non-standard SRID not supported!", + SerializerException.MessageKeys.WRONG_PROPERTY_VALUE, name, geoValue.toString()); + } + json.writeStartObject(); + json.writeStringField(Constants.ATTR_TYPE, geoValueTypeToJsonName.get(geoValue.getGeoType())); + json.writeFieldName(geoValue.getGeoType() == Geospatial.Type.GEOSPATIALCOLLECTION ? + Constants.JSON_GEOMETRIES : + Constants.JSON_COORDINATES); + json.writeStartArray(); + switch (geoValue.getGeoType()) { + case POINT: + writeGeoPoint(json, (Point) geoValue); + break; + case MULTIPOINT: + writeGeoPoints(json, (MultiPoint) geoValue); + break; + case LINESTRING: + writeGeoPoints(json, (LineString) geoValue); + break; + case MULTILINESTRING: + for (final LineString lineString : (MultiLineString) geoValue) { + json.writeStartArray(); + writeGeoPoints(json, lineString); + json.writeEndArray(); + } + break; + case POLYGON: + writeGeoPolygon(json, (Polygon) geoValue); + break; + case MULTIPOLYGON: + for (final Polygon polygon : (MultiPolygon) geoValue) { + json.writeStartArray(); + writeGeoPolygon(json, polygon); + json.writeEndArray(); + } + break; + case GEOSPATIALCOLLECTION: + for (final Geospatial element : (GeospatialCollection) geoValue) { + writeGeoValue(name, EdmPrimitiveTypeFactory.getInstance(element.getEdmPrimitiveTypeKind()), + element, isNullable, json); + } + break; + } + json.writeEndArray(); + json.writeEndObject(); + } + } + + private void writeGeoPoint(JsonGenerator json, final Point point) throws IOException { + json.writeNumber(point.getX()); + json.writeNumber(point.getY()); + if (point.getZ() != 0) { + json.writeNumber(point.getZ()); + } + } + + private void writeGeoPoints(JsonGenerator json, final ComposedGeospatial points) throws IOException { + for (final Point point : points) { + json.writeStartArray(); + writeGeoPoint(json, point); + json.writeEndArray(); + } + } + + // TODO: There could be a more strict verification that the lines describe boundaries + // and have the correct winding order. + // But arguably the better place for this is the constructor of the Polygon object. + private void writeGeoPolygon(JsonGenerator json, final Polygon polygon) throws IOException { + json.writeStartArray(); + writeGeoPoints(json, polygon.getExterior()); + json.writeEndArray(); + if (!polygon.getInterior().isEmpty()) { + json.writeStartArray(); + writeGeoPoints(json, polygon.getInterior()); + json.writeEndArray(); + } + } + protected void writeComplexValue(final ServiceMetadata metadata, final EdmComplexType type, final List properties, final Set> selectedPaths, final JsonGenerator json) throws IOException, SerializerException { - for (final String propertyName : type.getPropertyNames()) { + for (final String propertyName : type.getPropertyNames()) { final Property property = findProperty(propertyName, properties); if (selectedPaths == null || ExpandSelectHelper.isSelected(selectedPaths, propertyName)) { writeProperty(metadata, (EdmProperty) type.getProperty(propertyName), property, 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 index d91f3ff80..2f185948b 100644 --- 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 @@ -31,7 +31,9 @@ import java.io.ByteArrayInputStream; import java.io.InputStream; import java.math.BigDecimal; import java.util.ArrayList; +import java.util.Arrays; import java.util.Date; +import java.util.Iterator; import java.util.List; import org.apache.olingo.commons.api.Constants; @@ -44,6 +46,14 @@ import org.apache.olingo.commons.api.edm.EdmEntityType; import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind; import org.apache.olingo.commons.api.edm.EdmProperty; import org.apache.olingo.commons.api.edm.FullQualifiedName; +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.provider.CsdlMapping; import org.apache.olingo.commons.api.format.ContentType; import org.apache.olingo.server.api.OData; @@ -53,11 +63,13 @@ import org.apache.olingo.server.api.deserializer.ODataDeserializer; import org.apache.olingo.server.core.deserializer.AbstractODataDeserializerTest; import org.junit.Assert; import org.junit.Test; +import org.mockito.Mockito; public class ODataJsonDeserializerEntityTest extends AbstractODataDeserializerTest { private static final ContentType CONTENT_TYPE_JSON_IEEE754Compatible = ContentType.create(ContentType.JSON, ContentType.PARAMETER_IEEE754_COMPATIBLE, "true"); + private static final OData odata = OData.newInstance(); @Test public void emptyEntity() throws Exception { @@ -657,6 +669,231 @@ public class ODataJsonDeserializerEntityTest extends AbstractODataDeserializerTe assertEquals((short) 3, entity.getProperty("PropertyEnumString").getValue()); } + @Test + public void geoPoint() throws Exception { + final EdmEntityType entityType = mockEntityType(EdmPrimitiveTypeKind.GeometryPoint); + final String preamble = "{\"" + entityType.getPropertyNames().get(0) + "\":{"; + final Entity entity = deserialize(preamble + "\"type\":\"Point\",\"coordinates\":[1.25,2.75]}}", + entityType); + assertEquals(1, entity.getProperties().size()); + assertTrue(entity.getProperties().get(0).getValue() instanceof Point); + final Point point = (Point) entity.getProperties().get(0).getValue(); + assertEquals(Geospatial.Dimension.GEOMETRY, point.getDimension()); + assertEquals(1.25, point.getX(), 0); + assertEquals(2.75, point.getY(), 0); + + expectException(preamble + "}}", entityType, ContentType.JSON, + DeserializerException.MessageKeys.INVALID_VALUE_FOR_PROPERTY); + expectException(preamble + "\"type\":1}}", entityType, ContentType.JSON, + DeserializerException.MessageKeys.INVALID_VALUE_FOR_PROPERTY); + expectException(preamble + "\"type\":\"point\",\"coordinates\":null}}", entityType, ContentType.JSON, + DeserializerException.MessageKeys.INVALID_VALUE_FOR_PROPERTY); + expectException(preamble + "\"type\":\"LineString\"}}", entityType, ContentType.JSON, + DeserializerException.MessageKeys.INVALID_VALUE_FOR_PROPERTY); + expectException(preamble + "\"type\":\"Point\"}}", entityType, ContentType.JSON, + DeserializerException.MessageKeys.INVALID_VALUE_FOR_PROPERTY); + expectException(preamble + "\"type\":\"Point\",\"coord\":[]}}", entityType, ContentType.JSON, + DeserializerException.MessageKeys.UNKNOWN_CONTENT); + expectException(preamble + "\"type\":\"Point\",\"coordinates\":\"1 2\"}}", entityType, ContentType.JSON, + DeserializerException.MessageKeys.INVALID_VALUE_FOR_PROPERTY); + expectException(preamble + "\"type\":\"Point\",\"coordinates\":{\"x\":1,\"y\":2}}}", entityType, ContentType.JSON, + DeserializerException.MessageKeys.INVALID_VALUE_FOR_PROPERTY); + expectException(preamble + "\"type\":\"Point\",\"coordinates\":[]}}", entityType, ContentType.JSON, + DeserializerException.MessageKeys.INVALID_VALUE_FOR_PROPERTY); + expectException(preamble + "\"type\":\"Point\",\"coordinates\":[1]}}", entityType, ContentType.JSON, + DeserializerException.MessageKeys.INVALID_VALUE_FOR_PROPERTY); + expectException(preamble + "\"type\":\"Point\",\"coordinates\":[\"1\",2]}}", entityType, ContentType.JSON, + DeserializerException.MessageKeys.INVALID_VALUE_FOR_PROPERTY); + expectException(preamble + "\"type\":\"Point\",\"coordinates\":[1,\"2\"]}}", entityType, ContentType.JSON, + DeserializerException.MessageKeys.INVALID_VALUE_FOR_PROPERTY); + expectException(preamble + "\"type\":\"Point\",\"coordinates\":[1,2,\"3\"]}}", entityType, ContentType.JSON, + DeserializerException.MessageKeys.INVALID_VALUE_FOR_PROPERTY); + expectException(preamble + "\"type\":\"Point\",\"coordinates\":[1,2,3,4]}}", entityType, ContentType.JSON, + DeserializerException.MessageKeys.INVALID_VALUE_FOR_PROPERTY); + expectException(preamble + "\"type\":\"Point\",\"coordinates\":[12345678901234567,2]}}", entityType, + ContentType.JSON, DeserializerException.MessageKeys.INVALID_VALUE_FOR_PROPERTY); + expectException(preamble + "\"type\":\"Point\",\"coordinates\":[1,2],\"extra\":\"extra\"}}", entityType, + ContentType.JSON, DeserializerException.MessageKeys.UNKNOWN_CONTENT); + } + + @Test + public void geoMultiPoint() throws Exception { + final EdmEntityType entityType = mockEntityType(EdmPrimitiveTypeKind.GeographyMultiPoint); + final Entity entity = deserialize("{\"" + entityType.getPropertyNames().get(0) + "\":{" + + "\"type\":\"MultiPoint\",\"coordinates\":[[2.5,3.125,99],[3.5,4.125],[4.5,5.125]]}}", + entityType); + assertTrue(entity.getProperties().get(0).getValue() instanceof MultiPoint); + final MultiPoint multiPoint = (MultiPoint) entity.getProperties().get(0).getValue(); + assertEquals(Geospatial.Dimension.GEOGRAPHY, multiPoint.getDimension()); + Iterator iterator = multiPoint.iterator(); + final Point point1 = iterator.next(); + assertEquals(Geospatial.Dimension.GEOGRAPHY, point1.getDimension()); + assertEquals(2.5, point1.getX(), 0); + assertEquals(3.125, point1.getY(), 0); + assertEquals(99, point1.getZ(), 0); + final Point point2 = iterator.next(); + assertEquals(Geospatial.Dimension.GEOGRAPHY, point2.getDimension()); + assertEquals(3.5, point2.getX(), 0); + assertEquals(4.125, point2.getY(), 0); + final Point point3 = iterator.next(); + assertEquals(Geospatial.Dimension.GEOGRAPHY, point3.getDimension()); + assertEquals(4.5, point3.getX(), 0); + assertEquals(5.125, point3.getY(), 0); + assertFalse(iterator.hasNext()); + + expectException("{\"" + entityType.getPropertyNames().get(0) + "\":{" + + "\"type\":\"MultiPoint\",\"coordinates\":[{\"x\":1,\"y\":2}]}}", + entityType, ContentType.JSON, DeserializerException.MessageKeys.INVALID_VALUE_FOR_PROPERTY); + } + + @Test + public void geoLineString() throws Exception { + final EdmEntityType entityType = mockEntityType(EdmPrimitiveTypeKind.GeometryLineString); + final Entity entity = deserialize("{\"" + entityType.getPropertyNames().get(0) + "\":{" + + "\"type\":\"LineString\",\"coordinates\":[[1.0,1.0],[2.0,2.0]]}}", + entityType); + assertTrue(entity.getProperties().get(0).getValue() instanceof LineString); + final LineString lineString = (LineString) entity.getProperties().get(0).getValue(); + assertEquals(Geospatial.Dimension.GEOMETRY, lineString.getDimension()); + Iterator iterator = lineString.iterator(); + final Point point1 = iterator.next(); + assertEquals(Geospatial.Dimension.GEOMETRY, point1.getDimension()); + assertEquals(1, point1.getX(), 0); + assertEquals(1, point1.getY(), 0); + final Point point2 = iterator.next(); + assertEquals(Geospatial.Dimension.GEOMETRY, point2.getDimension()); + assertEquals(2, point2.getX(), 0); + assertEquals(2, point2.getY(), 0); + assertFalse(iterator.hasNext()); + + expectException("{\"" + entityType.getPropertyNames().get(0) + "\":{\"type\":\"LineString\"}}", entityType, + ContentType.JSON, DeserializerException.MessageKeys.INVALID_VALUE_FOR_PROPERTY); + // A proper line string has at least two points but the OData specification has another opinion + // so the following negative test would fail. + // expectException("{\"" + entityType.getPropertyNames().get(0) + // + "\":{\"type\":\"LineString\",\"coordinates\":[[1,2]]}}", entityType, ContentType.JSON, + // DeserializerException.MessageKeys.INVALID_VALUE_FOR_PROPERTY); + expectException("{\"" + entityType.getPropertyNames().get(0) + + "\":{\"type\":\"LineString\",\"coordinates\":null}}", entityType, ContentType.JSON, + DeserializerException.MessageKeys.INVALID_VALUE_FOR_PROPERTY); + } + + @Test + public void geoMultiLineString() throws Exception { + final EdmEntityType entityType = mockEntityType(EdmPrimitiveTypeKind.GeometryMultiLineString); + final Entity entity = deserialize("{\"" + entityType.getPropertyNames().get(0) + "\":{" + + "\"type\":\"MultiLineString\",\"coordinates\":[" + + "[[1.0,1.0],[2.0,2.0],[3.0,3.0],[4.0,4.0],[5.0,5.0]]," + + "[[99.5,101.5],[150.0,151.25]]]}}", + entityType); + assertTrue(entity.getProperties().get(0).getValue() instanceof MultiLineString); + final MultiLineString multiLineString = (MultiLineString) entity.getProperties().get(0).getValue(); + assertEquals(Geospatial.Dimension.GEOMETRY, multiLineString.getDimension()); + assertEquals(1, multiLineString.iterator().next().iterator().next().getY(), 0); + + expectException("{\"" + entityType.getPropertyNames().get(0) + + "\":{\"type\":\"MultiLineString\",\"coordinates\":null}}", entityType, + ContentType.JSON, DeserializerException.MessageKeys.INVALID_VALUE_FOR_PROPERTY); + expectException("{\"" + entityType.getPropertyNames().get(0) + + "\":{\"type\":\"MultiLineString\",\"coordinates\":\"1 2 3 4\"}}", entityType, + ContentType.JSON, DeserializerException.MessageKeys.INVALID_VALUE_FOR_PROPERTY); + expectException("{\"" + entityType.getPropertyNames().get(0) + + "\":{\"type\":\"MultiLineString\",\"coordinates\":[{\"first\":[[1,2],[3,4]]}]}}", entityType, + ContentType.JSON, DeserializerException.MessageKeys.INVALID_VALUE_FOR_PROPERTY); + } + + @Test + public void geoPolygon() throws Exception { + final EdmEntityType entityType = mockEntityType(EdmPrimitiveTypeKind.GeometryPolygon); + Entity entity = deserialize("{\"" + entityType.getPropertyNames().get(0) + "\":{" + + "\"type\":\"Polygon\",\"coordinates\":[[[0.0,0.0],[3.0,0.0],[3.0,3.0],[0.0,3.0],[0.0,0.0]]," + + "[[1.0,1.0],[1.0,2.0],[2.0,2.0],[2.0,1.0],[1.0,1.0]]]}}", + entityType); + assertTrue(entity.getProperties().get(0).getValue() instanceof Polygon); + Polygon polygon = (Polygon) entity.getProperties().get(0).getValue(); + assertEquals(Geospatial.Dimension.GEOMETRY, polygon.getDimension()); + assertEquals(0, polygon.getExterior().iterator().next().getX(), 0); + assertEquals(1, polygon.getInterior().iterator().next().getY(), 0); + + entity = deserialize("{\"" + entityType.getPropertyNames().get(0) + "\":{" + + "\"type\":\"Polygon\",\"coordinates\":[[[0,0],[3,0],[3,3],[0,3],[0,0]]]}}", + entityType); + polygon = (Polygon) entity.getProperties().get(0).getValue(); + assertTrue(polygon.getInterior().isEmpty()); + + expectException("{\"" + entityType.getPropertyNames().get(0) + "\":{" + + "\"type\":\"Polygon\",\"coordinates\":{\"ext\":[[0,0],[3,0],[0,3],[0,0]]}}}", entityType, + ContentType.JSON, DeserializerException.MessageKeys.INVALID_VALUE_FOR_PROPERTY); + expectException("{\"" + entityType.getPropertyNames().get(0) + "\":{" + + "\"type\":\"Polygon\",\"coordinates\":[[[0,0],[3,0],[3,3],[0,3]]]}}", entityType, + ContentType.JSON, DeserializerException.MessageKeys.INVALID_VALUE_FOR_PROPERTY); + expectException("{\"" + entityType.getPropertyNames().get(0) + "\":{" + + "\"type\":\"Polygon\",\"coordinates\":[[[0,0],[3,0],[3,3],[0,3],[42,87]]]}}", entityType, + ContentType.JSON, DeserializerException.MessageKeys.INVALID_VALUE_FOR_PROPERTY); + expectException("{\"" + entityType.getPropertyNames().get(0) + "\":{" + + "\"type\":\"Polygon\",\"coordinates\":[[[0,0],[3,0],[3,3],[0,3],[0,0]]," + + "[[1,1],[1,2],[2,2],[2,1],[1,1]]," + + "[[1,1],[1,2],[2,2],[2,1],[1,1]]]}}", entityType, + ContentType.JSON, DeserializerException.MessageKeys.INVALID_VALUE_FOR_PROPERTY); + } + + @Test + public void geoMultiPolygon() throws Exception { + final EdmEntityType entityType = mockEntityType(EdmPrimitiveTypeKind.GeometryMultiPolygon); + final Entity entity = deserialize("{\"" + entityType.getPropertyNames().get(0) + "\":{" + + "\"type\":\"MultiPolygon\",\"coordinates\":[" + + "[[[0.0,0.0],[3.0,0.0],[3.0,3.0],[0.0,3.0],[0.0,0.0]]," + + "[[1.0,1.0],[1.0,2.0],[2.0,2.0],[2.0,1.0],[1.0,1.0]]]," + + "[[[0.0,0.0],[30.0,0.0],[0.0,30.0],[0.0,0.0]]]]}}", + entityType); + final MultiPolygon multiPolygon = (MultiPolygon) entity.getProperties().get(0).getValue(); + assertEquals(1, multiPolygon.iterator().next().getInterior().iterator().next().getX(), 0); + + expectException("{\"" + entityType.getPropertyNames().get(0) + "\":{" + + "\"type\":\"MultiPolygon\",\"coordinates\":[{\"first\":[[[0,0],[3,0],[3,3],[0,3],[0,0]]]}]}}", entityType, + ContentType.JSON, DeserializerException.MessageKeys.INVALID_VALUE_FOR_PROPERTY); + } + + @Test + public void geoCollection() throws Exception { + final EdmEntityType entityType = mockEntityType(EdmPrimitiveTypeKind.GeometryCollection); + final Entity entity = deserialize("{\"" + entityType.getPropertyNames().get(0) + "\":{" + + "\"type\":\"GeometryCollection\",\"geometries\":[" + + "{\"type\":\"Point\",\"coordinates\":[100.0,0.0]}," + + "{\"type\":\"LineString\",\"coordinates\":[[101.0,0.0],[102.0,1.0]]}]}}", + entityType); + assertTrue(entity.getProperties().get(0).getValue() instanceof GeospatialCollection); + GeospatialCollection collection = (GeospatialCollection) entity.getProperties().get(0).getValue(); + assertEquals(Geospatial.Dimension.GEOMETRY, collection.getDimension()); + Iterator iterator = collection.iterator(); + final Geospatial point = iterator.next(); + assertEquals(Geospatial.Dimension.GEOMETRY, point.getDimension()); + assertEquals(Geospatial.Type.POINT, point.getGeoType()); + assertEquals(100, ((Point) point).getX(), 0); + final Geospatial line = iterator.next(); + assertEquals(Geospatial.Dimension.GEOMETRY, line.getDimension()); + assertEquals(Geospatial.Type.LINESTRING, line.getGeoType()); + assertEquals(101, ((LineString) line).iterator().next().getX(), 0); + + expectException("{\"" + entityType.getPropertyNames().get(0) + "\":{" + + "\"type\":\"GeometryCollection\",\"coordinates\":[0,0]}}", entityType, + ContentType.JSON, DeserializerException.MessageKeys.UNKNOWN_CONTENT); + expectException("{\"" + entityType.getPropertyNames().get(0) + "\":{" + + "\"type\":\"GeometryCollection\",\"geometries\":[[0,0]]}}", entityType, + ContentType.JSON, DeserializerException.MessageKeys.INVALID_VALUE_FOR_PROPERTY); + } + + private EdmEntityType mockEntityType(final EdmPrimitiveTypeKind typeKind) { + EdmProperty property = Mockito.mock(EdmProperty.class); + final String name = "Property" + typeKind.name(); + Mockito.when(property.getType()).thenReturn(odata.createPrimitiveTypeInstance(typeKind)); + EdmEntityType entityType = Mockito.mock(EdmEntityType.class); + Mockito.when(entityType.getFullQualifiedName()).thenReturn(new FullQualifiedName(NAMESPACE, "entityType")); + Mockito.when(entityType.getPropertyNames()).thenReturn(Arrays.asList(name)); + Mockito.when(entityType.getProperty(name)).thenReturn(property); + return entityType; + } + @Test public void mappingTest() throws Exception { EdmEntityType entityType = mock(EdmEntityType.class); @@ -670,22 +907,21 @@ public class ODataJsonDeserializerEntityTest extends AbstractODataDeserializerTe EdmProperty propertyDate = mock(EdmProperty.class); when(propertyDate.getName()).thenReturn("PropertyDate"); when(propertyDate.getMapping()).thenReturn(mapping); - when(propertyDate.getType()).thenReturn( - OData.newInstance().createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Date)); + when(propertyDate.getType()).thenReturn(odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.Date)); when(entityType.getProperty("PropertyDate")).thenReturn(propertyDate); EdmProperty propertyDateTimeOffset = mock(EdmProperty.class); when(propertyDateTimeOffset.getName()).thenReturn("PropertyDateTimeOffset"); when(propertyDateTimeOffset.getMapping()).thenReturn(mapping); when(propertyDateTimeOffset.getType()).thenReturn( - OData.newInstance().createPrimitiveTypeInstance(EdmPrimitiveTypeKind.DateTimeOffset)); + odata.createPrimitiveTypeInstance(EdmPrimitiveTypeKind.DateTimeOffset)); when(entityType.getProperty("PropertyDateTimeOffset")).thenReturn(propertyDateTimeOffset); String entityString = "{\"PropertyDate\":\"2012-12-03\"," + "\"PropertyDateTimeOffset\":\"2012-12-03T07:16:23Z\"}"; InputStream stream = new ByteArrayInputStream(entityString.getBytes()); - ODataDeserializer deserializer = OData.newInstance().createDeserializer(ContentType.JSON, metadata); + ODataDeserializer deserializer = odata.createDeserializer(ContentType.JSON, metadata); Entity entity = deserializer.entity(stream, entityType).getEntity(); assertNotNull(entity); List properties = entity.getProperties(); @@ -844,7 +1080,7 @@ public class ODataJsonDeserializerEntityTest extends AbstractODataDeserializerTe } @Test - public void unkownContentInEntity() throws Exception { + public void unknownContentInEntity() throws Exception { final String entityString = "{" + "\"PropertyInt16\":32767," + "\"unknown\": 12," @@ -860,7 +1096,7 @@ public class ODataJsonDeserializerEntityTest extends AbstractODataDeserializerTe } @Test - public void unkownContentInComplexProperty() throws Exception { + public void unknownContentInComplexProperty() throws Exception { final String entityString = "{" + "\"PropertyInt16\":32767," + "\"CollPropertyString\":" @@ -875,7 +1111,7 @@ public class ODataJsonDeserializerEntityTest extends AbstractODataDeserializerTe } @Test - public void unkownContentInComplexCollectionProperty() throws Exception { + public void unknownContentInComplexCollectionProperty() throws Exception { final String entityString = "{" + "\"PropertyInt16\":32767," + "\"CollPropertyString\":" @@ -1363,15 +1599,18 @@ public class ODataJsonDeserializerEntityTest extends AbstractODataDeserializerTe protected static Entity deserialize(final InputStream stream, final String entityTypeName, final ContentType contentType) throws DeserializerException { - return OData.newInstance().createDeserializer(contentType, metadata) - .entity(stream, edm.getEntityType(new FullQualifiedName(NAMESPACE, entityTypeName))) - .getEntity(); + return deserializeWithResult(stream, entityTypeName, contentType).getEntity(); } - + protected static DeserializerResult deserializeWithResult(final InputStream stream, final String entityTypeName, final ContentType contentType) throws DeserializerException { - return OData.newInstance().createDeserializer(contentType, metadata) - .entity(stream, edm.getEntityType(new FullQualifiedName(NAMESPACE, entityTypeName))); + final EdmEntityType entityType = edm.getEntityType(new FullQualifiedName(NAMESPACE, entityTypeName)); + return deserializeWithResult(stream, entityType, contentType); + } + + protected static DeserializerResult deserializeWithResult(final InputStream stream, final EdmEntityType entityType, + final ContentType contentType) throws DeserializerException { + return odata.createDeserializer(contentType, metadata).entity(stream, entityType); } private static Entity deserialize(final String entityString, final String entityTypeName, @@ -1384,22 +1623,34 @@ public class ODataJsonDeserializerEntityTest extends AbstractODataDeserializerTe return deserialize(entityString, entityTypeName, ContentType.JSON); } + private Entity deserialize(final String entityString, final EdmEntityType entityType) + throws DeserializerException { + return deserializeWithResult(new ByteArrayInputStream(entityString.getBytes()), entityType, ContentType.JSON) + .getEntity(); + } + private static void checkPropertyJsonType(final String entityString) throws DeserializerException { expectException(entityString, "ETAllPrim", DeserializerException.MessageKeys.INVALID_VALUE_FOR_PROPERTY); } + protected static void expectException(final String entityString, final String entityTypeName, + final ContentType contentType, final DeserializerException.MessageKeys messageKey) { + expectException(entityString, + edm.getEntityType(new FullQualifiedName(NAMESPACE, entityTypeName)), contentType, messageKey); + } + protected static void expectException(final String entityString, final String entityTypeName, final DeserializerException.MessageKeys messageKey) { expectException(entityString, entityTypeName, ContentType.JSON, messageKey); } - private static void expectException(final String entityString, final String entityTypeName, + private static void expectException(final String entityString, final EdmEntityType entityType, final ContentType contentType, final DeserializerException.MessageKeys messageKey) { try { - deserialize(entityString, entityTypeName, contentType); + deserializeWithResult(new ByteArrayInputStream(entityString.getBytes()), entityType, contentType); fail("Expected exception not thrown."); } catch (final DeserializerException e) { assertEquals(messageKey, e.getMessageKey()); } - } + } } diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java index 82f49aa9c..cf29d5f3b 100644 --- a/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java +++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java @@ -18,6 +18,9 @@ */ package org.apache.olingo.server.core.serializer.json; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + import java.io.IOException; import java.io.InputStream; import java.net.URI; @@ -44,7 +47,17 @@ import org.apache.olingo.commons.api.edm.EdmEntityContainer; import org.apache.olingo.commons.api.edm.EdmEntitySet; import org.apache.olingo.commons.api.edm.EdmEntityType; import org.apache.olingo.commons.api.edm.EdmPrimitiveType; +import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind; import org.apache.olingo.commons.api.edm.EdmProperty; +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.api.edm.geo.Geospatial.Dimension; +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.edmx.EdmxReference; import org.apache.olingo.commons.api.format.ContentType; import org.apache.olingo.server.api.OData; @@ -647,13 +660,13 @@ public class ODataJsonSerializerTest { final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESTwoPrim"); final Entity entity = data.readAll(edmEntitySet).getEntities().get(0); InputStream result = serializer - .entity(metadata, edmEntitySet.getEntityType(), entity, EntitySerializerOptions.with() - .contextURL(ContextURL.with().entitySet(edmEntitySet).suffix(Suffix.ENTITY).build()) - .build()).getContent(); + .entity(metadata, edmEntitySet.getEntityType(), entity, EntitySerializerOptions.with() + .contextURL(ContextURL.with().entitySet(edmEntitySet).suffix(Suffix.ENTITY).build()) + .build()).getContent(); final String resultString = IOUtils.toString(result); - final String expectedResult = "{\"@odata.context\":\"$metadata#ESTwoPrim/$entity\"," + - "\"@odata.metadataEtag\":\"W/\\\"metadataETag\\\"\"," + - "\"PropertyInt16\":32766,\"PropertyString\":\"Test String1\"}"; + final String expectedResult = "{\"@odata.context\":\"$metadata#ESTwoPrim/$entity\"," + + "\"@odata.metadataEtag\":\"W/\\\"metadataETag\\\"\"," + + "\"PropertyInt16\":32766,\"PropertyString\":\"Test String1\"}"; Assert.assertEquals(expectedResult, resultString); } @@ -673,67 +686,67 @@ public class ODataJsonSerializerTest { + "\"value\":[{\"@odata.type\":\"#olingo.odata.test1.ETTwoPrim\",\"@odata.id\":\"ESTwoPrim(32766)\"," + "\"PropertyInt16@odata.type\":\"#Int16\",\"PropertyInt16\":32766," + "\"PropertyString\":\"Test String1\"," - +"\"#olingo.odata.test1.BAETTwoPrimRTString\":" - +"{\"title\":\"olingo.odata.test1.BAETTwoPrimRTString\"," - +"\"target\":\"ESTwoPrim(32766)/olingo.odata.test1.BAETTwoPrimRTString\"}," - +"\"#olingo.odata.test1.BAETTwoPrimRTCollString\":" - +"{\"title\":\"olingo.odata.test1.BAETTwoPrimRTCollString\"," - +"\"target\":\"ESTwoPrim(32766)/olingo.odata.test1.BAETTwoPrimRTCollString\"}," - +"\"#olingo.odata.test1.BAETTwoPrimRTCTAllPrim\":" - +"{\"title\":\"olingo.odata.test1.BAETTwoPrimRTCTAllPrim\"," - +"\"target\":\"ESTwoPrim(32766)/olingo.odata.test1.BAETTwoPrimRTCTAllPrim\"}," - +"\"#olingo.odata.test1.BAETTwoPrimRTCollCTAllPrim\":" - +"{\"title\":\"olingo.odata.test1.BAETTwoPrimRTCollCTAllPrim\"," - +"\"target\":\"ESTwoPrim(32766)/olingo.odata.test1.BAETTwoPrimRTCollCTAllPrim\"}" + + "\"#olingo.odata.test1.BAETTwoPrimRTString\":" + + "{\"title\":\"olingo.odata.test1.BAETTwoPrimRTString\"," + + "\"target\":\"ESTwoPrim(32766)/olingo.odata.test1.BAETTwoPrimRTString\"}," + + "\"#olingo.odata.test1.BAETTwoPrimRTCollString\":" + + "{\"title\":\"olingo.odata.test1.BAETTwoPrimRTCollString\"," + + "\"target\":\"ESTwoPrim(32766)/olingo.odata.test1.BAETTwoPrimRTCollString\"}," + + "\"#olingo.odata.test1.BAETTwoPrimRTCTAllPrim\":" + + "{\"title\":\"olingo.odata.test1.BAETTwoPrimRTCTAllPrim\"," + + "\"target\":\"ESTwoPrim(32766)/olingo.odata.test1.BAETTwoPrimRTCTAllPrim\"}," + + "\"#olingo.odata.test1.BAETTwoPrimRTCollCTAllPrim\":" + + "{\"title\":\"olingo.odata.test1.BAETTwoPrimRTCollCTAllPrim\"," + + "\"target\":\"ESTwoPrim(32766)/olingo.odata.test1.BAETTwoPrimRTCollCTAllPrim\"}" + "}," + "{\"@odata.type\":\"#olingo.odata.test1.ETTwoPrim\",\"@odata.id\":\"ESTwoPrim(-365)\"," + "\"PropertyInt16@odata.type\":\"#Int16\",\"PropertyInt16\":-365," + "\"PropertyString\":\"Test String2\"," + "\"NavPropertyETAllPrimMany@odata.navigationLink\":\"ESTwoPrim(-365)/NavPropertyETAllPrimMany\"," - +"\"#olingo.odata.test1.BAETTwoPrimRTString\":" - +"{\"title\":\"olingo.odata.test1.BAETTwoPrimRTString\"," - +"\"target\":\"ESTwoPrim(-365)/olingo.odata.test1.BAETTwoPrimRTString\"}," - +"\"#olingo.odata.test1.BAETTwoPrimRTCollString\":" - +"{\"title\":\"olingo.odata.test1.BAETTwoPrimRTCollString\"," - +"\"target\":\"ESTwoPrim(-365)/olingo.odata.test1.BAETTwoPrimRTCollString\"}," - +"\"#olingo.odata.test1.BAETTwoPrimRTCTAllPrim\":" - +"{\"title\":\"olingo.odata.test1.BAETTwoPrimRTCTAllPrim\"," - +"\"target\":\"ESTwoPrim(-365)/olingo.odata.test1.BAETTwoPrimRTCTAllPrim\"}," - +"\"#olingo.odata.test1.BAETTwoPrimRTCollCTAllPrim\":" - +"{\"title\":\"olingo.odata.test1.BAETTwoPrimRTCollCTAllPrim\"," - +"\"target\":\"ESTwoPrim(-365)/olingo.odata.test1.BAETTwoPrimRTCollCTAllPrim\"}}," + + "\"#olingo.odata.test1.BAETTwoPrimRTString\":" + + "{\"title\":\"olingo.odata.test1.BAETTwoPrimRTString\"," + + "\"target\":\"ESTwoPrim(-365)/olingo.odata.test1.BAETTwoPrimRTString\"}," + + "\"#olingo.odata.test1.BAETTwoPrimRTCollString\":" + + "{\"title\":\"olingo.odata.test1.BAETTwoPrimRTCollString\"," + + "\"target\":\"ESTwoPrim(-365)/olingo.odata.test1.BAETTwoPrimRTCollString\"}," + + "\"#olingo.odata.test1.BAETTwoPrimRTCTAllPrim\":" + + "{\"title\":\"olingo.odata.test1.BAETTwoPrimRTCTAllPrim\"," + + "\"target\":\"ESTwoPrim(-365)/olingo.odata.test1.BAETTwoPrimRTCTAllPrim\"}," + + "\"#olingo.odata.test1.BAETTwoPrimRTCollCTAllPrim\":" + + "{\"title\":\"olingo.odata.test1.BAETTwoPrimRTCollCTAllPrim\"," + + "\"target\":\"ESTwoPrim(-365)/olingo.odata.test1.BAETTwoPrimRTCollCTAllPrim\"}}," + "{\"@odata.type\":\"#olingo.odata.test1.ETTwoPrim\",\"@odata.id\":\"ESTwoPrim(-32766)\"," + "\"PropertyInt16@odata.type\":\"#Int16\",\"PropertyInt16\":-32766," + "\"PropertyString\":null," - +"\"#olingo.odata.test1.BAETTwoPrimRTString\":" - +"{\"title\":\"olingo.odata.test1.BAETTwoPrimRTString\"," - +"\"target\":\"ESTwoPrim(-32766)/olingo.odata.test1.BAETTwoPrimRTString\"}," - +"\"#olingo.odata.test1.BAETTwoPrimRTCollString\":" - +"{\"title\":\"olingo.odata.test1.BAETTwoPrimRTCollString\"," - +"\"target\":\"ESTwoPrim(-32766)/olingo.odata.test1.BAETTwoPrimRTCollString\"}," - +"\"#olingo.odata.test1.BAETTwoPrimRTCTAllPrim\":" - +"{\"title\":\"olingo.odata.test1.BAETTwoPrimRTCTAllPrim\"," - +"\"target\":\"ESTwoPrim(-32766)/olingo.odata.test1.BAETTwoPrimRTCTAllPrim\"}," - +"\"#olingo.odata.test1.BAETTwoPrimRTCollCTAllPrim\":" - +"{\"title\":\"olingo.odata.test1.BAETTwoPrimRTCollCTAllPrim\"," - +"\"target\":\"ESTwoPrim(-32766)/olingo.odata.test1.BAETTwoPrimRTCollCTAllPrim\"}}," + + "\"#olingo.odata.test1.BAETTwoPrimRTString\":" + + "{\"title\":\"olingo.odata.test1.BAETTwoPrimRTString\"," + + "\"target\":\"ESTwoPrim(-32766)/olingo.odata.test1.BAETTwoPrimRTString\"}," + + "\"#olingo.odata.test1.BAETTwoPrimRTCollString\":" + + "{\"title\":\"olingo.odata.test1.BAETTwoPrimRTCollString\"," + + "\"target\":\"ESTwoPrim(-32766)/olingo.odata.test1.BAETTwoPrimRTCollString\"}," + + "\"#olingo.odata.test1.BAETTwoPrimRTCTAllPrim\":" + + "{\"title\":\"olingo.odata.test1.BAETTwoPrimRTCTAllPrim\"," + + "\"target\":\"ESTwoPrim(-32766)/olingo.odata.test1.BAETTwoPrimRTCTAllPrim\"}," + + "\"#olingo.odata.test1.BAETTwoPrimRTCollCTAllPrim\":" + + "{\"title\":\"olingo.odata.test1.BAETTwoPrimRTCollCTAllPrim\"," + + "\"target\":\"ESTwoPrim(-32766)/olingo.odata.test1.BAETTwoPrimRTCollCTAllPrim\"}}," + "{\"@odata.type\":\"#olingo.odata.test1.ETTwoPrim\",\"@odata.id\":\"ESTwoPrim(32767)\"," + "\"PropertyInt16@odata.type\":\"#Int16\",\"PropertyInt16\":32767," + "\"PropertyString\":\"Test String4\"," - +"\"NavPropertyETAllPrimOne@odata.navigationLink\":\"ESAllPrim(32767)\"," - +"\"#olingo.odata.test1.BAETTwoPrimRTString\":" - +"{\"title\":\"olingo.odata.test1.BAETTwoPrimRTString\"," - +"\"target\":\"ESTwoPrim(32767)/olingo.odata.test1.BAETTwoPrimRTString\"}," - +"\"#olingo.odata.test1.BAETTwoPrimRTCollString\":" - +"{\"title\":\"olingo.odata.test1.BAETTwoPrimRTCollString\"," - +"\"target\":\"ESTwoPrim(32767)/olingo.odata.test1.BAETTwoPrimRTCollString\"}," - +"\"#olingo.odata.test1.BAETTwoPrimRTCTAllPrim\":" - +"{\"title\":\"olingo.odata.test1.BAETTwoPrimRTCTAllPrim\"," - +"\"target\":\"ESTwoPrim(32767)/olingo.odata.test1.BAETTwoPrimRTCTAllPrim\"}," - +"\"#olingo.odata.test1.BAETTwoPrimRTCollCTAllPrim\":" - +"{\"title\":\"olingo.odata.test1.BAETTwoPrimRTCollCTAllPrim\"," - +"\"target\":\"ESTwoPrim(32767)/olingo.odata.test1.BAETTwoPrimRTCollCTAllPrim\"}" - +"}]}"; + + "\"NavPropertyETAllPrimOne@odata.navigationLink\":\"ESAllPrim(32767)\"," + + "\"#olingo.odata.test1.BAETTwoPrimRTString\":" + + "{\"title\":\"olingo.odata.test1.BAETTwoPrimRTString\"," + + "\"target\":\"ESTwoPrim(32767)/olingo.odata.test1.BAETTwoPrimRTString\"}," + + "\"#olingo.odata.test1.BAETTwoPrimRTCollString\":" + + "{\"title\":\"olingo.odata.test1.BAETTwoPrimRTCollString\"," + + "\"target\":\"ESTwoPrim(32767)/olingo.odata.test1.BAETTwoPrimRTCollString\"}," + + "\"#olingo.odata.test1.BAETTwoPrimRTCTAllPrim\":" + + "{\"title\":\"olingo.odata.test1.BAETTwoPrimRTCTAllPrim\"," + + "\"target\":\"ESTwoPrim(32767)/olingo.odata.test1.BAETTwoPrimRTCTAllPrim\"}," + + "\"#olingo.odata.test1.BAETTwoPrimRTCollCTAllPrim\":" + + "{\"title\":\"olingo.odata.test1.BAETTwoPrimRTCollCTAllPrim\"," + + "\"target\":\"ESTwoPrim(32767)/olingo.odata.test1.BAETTwoPrimRTCollCTAllPrim\"}" + + "}]}"; Assert.assertEquals(expectedResult, resultString); } @@ -972,20 +985,20 @@ public class ODataJsonSerializerTest { .build()).getContent(); final String resultString = IOUtils.toString(result); final String expected = "{" - + "\"@odata.context\":\"$metadata#ESFourKeyAlias" - + "(PropertyInt16,PropertyCompComp/PropertyComp/PropertyString)\"," - + "\"@odata.metadataEtag\":\"W/\\\"metadataETag\\\"\"," - + "\"value\":[" - + "{" - + "\"@odata.id\":\"" - + "ESFourKeyAlias(PropertyInt16=1,KeyAlias1=11,KeyAlias2='Num11',KeyAlias3='Num111')\"," - + "\"PropertyInt16\":1," - + "\"PropertyCompComp\":{" - + "\"PropertyComp\":{" - + "\"@odata.type\":\"#olingo.odata.test1.CTBase\"," - + "\"PropertyString\":\"Num111\"" - + "}}}]}"; - + + "\"@odata.context\":\"$metadata#ESFourKeyAlias" + + "(PropertyInt16,PropertyCompComp/PropertyComp/PropertyString)\"," + + "\"@odata.metadataEtag\":\"W/\\\"metadataETag\\\"\"," + + "\"value\":[" + + "{" + + "\"@odata.id\":\"" + + "ESFourKeyAlias(PropertyInt16=1,KeyAlias1=11,KeyAlias2='Num11',KeyAlias3='Num111')\"," + + "\"PropertyInt16\":1," + + "\"PropertyCompComp\":{" + + "\"PropertyComp\":{" + + "\"@odata.type\":\"#olingo.odata.test1.CTBase\"," + + "\"PropertyString\":\"Num111\"" + + "}}}]}"; + Assert.assertEquals(expected, resultString); } @@ -1000,24 +1013,24 @@ public class ODataJsonSerializerTest { .contextURL(ContextURL.with().entitySet(edmEntitySet).build()) .build()).getContent(); final String resultString = IOUtils.toString(result); - - String expected = "{" - + "\"@odata.context\":\"$metadata#ESFourKeyAlias\"," - + "\"@odata.metadataEtag\":\"W/\\\"metadataETag\\\"\"," - + "\"value\":[{" - + "\"PropertyInt16\":1," - + "\"PropertyComp\":{" - + "\"PropertyInt16\":11," - + "\"PropertyString\":\"Num11\"" - + "}," - + "\"PropertyCompComp\":{" - + "\"PropertyComp\":{" - + "\"@odata.type\":\"#olingo.odata.test1.CTBase\"," - + "\"PropertyInt16\":111," - + "\"PropertyString\":\"Num111\"," - + "\"AdditionalPropString\":\"Test123\"" - + "}}}]}"; - + + final String expected = "{" + + "\"@odata.context\":\"$metadata#ESFourKeyAlias\"," + + "\"@odata.metadataEtag\":\"W/\\\"metadataETag\\\"\"," + + "\"value\":[{" + + "\"PropertyInt16\":1," + + "\"PropertyComp\":{" + + "\"PropertyInt16\":11," + + "\"PropertyString\":\"Num11\"" + + "}," + + "\"PropertyCompComp\":{" + + "\"PropertyComp\":{" + + "\"@odata.type\":\"#olingo.odata.test1.CTBase\"," + + "\"PropertyInt16\":111," + + "\"PropertyString\":\"Num111\"," + + "\"AdditionalPropString\":\"Test123\"" + + "}}}]}"; + Assert.assertEquals(expected, resultString); } @@ -1058,127 +1071,127 @@ public class ODataJsonSerializerTest { Assert.assertEquals(expected, resultString); } - @Test - public void selectComplexNestedCollectionOfComplexWithMetadataFull() throws Exception{ - final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESCompCollComp"); - final EntityCollection entitySet = data.readAll(edmEntitySet); - InputStream result = serializerFullMetadata - .entityCollection(metadata, edmEntitySet.getEntityType(), entitySet, + @Test + public void selectComplexNestedCollectionOfComplexWithMetadataFull() throws Exception{ + final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESCompCollComp"); + final EntityCollection entitySet = data.readAll(edmEntitySet); + InputStream result = serializerFullMetadata + .entityCollection(metadata, edmEntitySet.getEntityType(), entitySet, EntityCollectionSerializerOptions.with() .contextURL(ContextURL.with().entitySet(edmEntitySet).build()) .build()) - .getContent(); - final String resultString = IOUtils.toString(result); - final String expectedResult = "{\"@odata.context\":\"$metadata#ESCompCollComp\"," - + "\"@odata.metadataEtag\":\"W/\\\"metadataETag\\\"\"," - + "\"value\":[{\"@odata.type\":\"#olingo.odata.test1.ETCompCollComp\"," - + "\"@odata.id\":\"ESCompCollComp(32767)\"," - + "\"PropertyInt16@odata.type\":\"#Int16\",\"PropertyInt16\":32767," - + "\"PropertyComp\":{" - + "\"@odata.type\":\"#olingo.odata.test1.CTCompCollComp\"," - + "\"CollPropertyComp@odata.type\":\"#Collection(olingo.odata.test1.CTTwoPrim)\"," - + "\"CollPropertyComp\":[" - + "{" - + "\"@odata.type\":\"#olingo.odata.test1.CTTwoPrim\"," - + "\"PropertyInt16@odata.type\":\"#Int16\"," - +"\"PropertyInt16\":555," - +"\"PropertyString\":\"1 Test Complex in Complex Property\"" - +"},{" - +"\"@odata.type\":\"#olingo.odata.test1.CTTwoPrim\"," - +"\"PropertyInt16@odata.type\":\"#Int16\"," - +"\"PropertyInt16\":666," - +"\"PropertyString\":\"2 Test Complex in Complex Property\"" - +"},{" - +"\"@odata.type\":\"#olingo.odata.test1.CTTwoPrim\"," - +"\"PropertyInt16@odata.type\":\"#Int16\"," - +"\"PropertyInt16\":777," - +"\"PropertyString\":\"3 Test Complex in Complex Property\"" - +"}]}},{" - +"\"@odata.type\":\"#olingo.odata.test1.ETCompCollComp\"," - +"\"@odata.id\":\"ESCompCollComp(12345)\"," - +"\"PropertyInt16@odata.type\":\"#Int16\"," - +"\"PropertyInt16\":12345," - +"\"PropertyComp\":{" - +"\"@odata.type\":\"#olingo.odata.test1.CTCompCollComp\"," - +"\"CollPropertyComp@odata.type\":\"#Collection(olingo.odata.test1.CTTwoPrim)\"," - +"\"CollPropertyComp\":[" - +"{" - +"\"@odata.type\":\"#olingo.odata.test1.CTTwoPrim\"," - +"\"PropertyInt16@odata.type\":\"#Int16\"," - +"\"PropertyInt16\":888," - +"\"PropertyString\":\"11 Test Complex in Complex Property\"" - +"},{" - +"\"@odata.type\":\"#olingo.odata.test1.CTTwoPrim\"," - +"\"PropertyInt16@odata.type\":\"#Int16\"," - +"\"PropertyInt16\":999," - +"\"PropertyString\":\"12 Test Complex in Complex Property\"" - +"},{" - +"\"@odata.type\":\"#olingo.odata.test1.CTTwoPrim\"," - +"\"PropertyInt16@odata.type\":\"#Int16\"," - +"\"PropertyInt16\":0," - +"\"PropertyString\":\"13 Test Complex in Complex Property\"" - +"}]}}]}"; - Assert.assertEquals(expectedResult, resultString); - } - - @Test - public void selectComplexNestedCollectionOfComplexWithMetadataMinimal() throws Exception{ - final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESCompCollComp"); - final EntityCollection entitySet = data.readAll(edmEntitySet); - InputStream result = serializer - .entityCollection(metadata, edmEntitySet.getEntityType(), entitySet, + .getContent(); + final String resultString = IOUtils.toString(result); + final String expectedResult = "{\"@odata.context\":\"$metadata#ESCompCollComp\"," + + "\"@odata.metadataEtag\":\"W/\\\"metadataETag\\\"\"," + + "\"value\":[{\"@odata.type\":\"#olingo.odata.test1.ETCompCollComp\"," + + "\"@odata.id\":\"ESCompCollComp(32767)\"," + + "\"PropertyInt16@odata.type\":\"#Int16\",\"PropertyInt16\":32767," + + "\"PropertyComp\":{" + + "\"@odata.type\":\"#olingo.odata.test1.CTCompCollComp\"," + + "\"CollPropertyComp@odata.type\":\"#Collection(olingo.odata.test1.CTTwoPrim)\"," + + "\"CollPropertyComp\":[" + + "{" + + "\"@odata.type\":\"#olingo.odata.test1.CTTwoPrim\"," + + "\"PropertyInt16@odata.type\":\"#Int16\"," + + "\"PropertyInt16\":555," + + "\"PropertyString\":\"1 Test Complex in Complex Property\"" + + "},{" + + "\"@odata.type\":\"#olingo.odata.test1.CTTwoPrim\"," + + "\"PropertyInt16@odata.type\":\"#Int16\"," + + "\"PropertyInt16\":666," + + "\"PropertyString\":\"2 Test Complex in Complex Property\"" + + "},{" + + "\"@odata.type\":\"#olingo.odata.test1.CTTwoPrim\"," + + "\"PropertyInt16@odata.type\":\"#Int16\"," + + "\"PropertyInt16\":777," + + "\"PropertyString\":\"3 Test Complex in Complex Property\"" + + "}]}},{" + + "\"@odata.type\":\"#olingo.odata.test1.ETCompCollComp\"," + + "\"@odata.id\":\"ESCompCollComp(12345)\"," + + "\"PropertyInt16@odata.type\":\"#Int16\"," + + "\"PropertyInt16\":12345," + + "\"PropertyComp\":{" + + "\"@odata.type\":\"#olingo.odata.test1.CTCompCollComp\"," + + "\"CollPropertyComp@odata.type\":\"#Collection(olingo.odata.test1.CTTwoPrim)\"," + + "\"CollPropertyComp\":[" + + "{" + + "\"@odata.type\":\"#olingo.odata.test1.CTTwoPrim\"," + + "\"PropertyInt16@odata.type\":\"#Int16\"," + + "\"PropertyInt16\":888," + + "\"PropertyString\":\"11 Test Complex in Complex Property\"" + + "},{" + + "\"@odata.type\":\"#olingo.odata.test1.CTTwoPrim\"," + + "\"PropertyInt16@odata.type\":\"#Int16\"," + + "\"PropertyInt16\":999," + + "\"PropertyString\":\"12 Test Complex in Complex Property\"" + + "},{" + + "\"@odata.type\":\"#olingo.odata.test1.CTTwoPrim\"," + + "\"PropertyInt16@odata.type\":\"#Int16\"," + + "\"PropertyInt16\":0," + + "\"PropertyString\":\"13 Test Complex in Complex Property\"" + + "}]}}]}"; + Assert.assertEquals(expectedResult, resultString); + } + + @Test + public void selectComplexNestedCollectionOfComplexWithMetadataMinimal() throws Exception{ + final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESCompCollComp"); + final EntityCollection entitySet = data.readAll(edmEntitySet); + InputStream result = serializer + .entityCollection(metadata, edmEntitySet.getEntityType(), entitySet, EntityCollectionSerializerOptions.with() .contextURL(ContextURL.with().entitySet(edmEntitySet).build()) .build()) - .getContent(); - final String resultString = IOUtils.toString(result); - final String expectedResult = "{\"@odata.context\":\"$metadata#ESCompCollComp\"," - + "\"@odata.metadataEtag\":\"W/\\\"metadataETag\\\"\"," - + "\"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\"" - +"}]}}]}"; - Assert.assertEquals(expectedResult, resultString); - } - - @Test - public void selectComplexNestedCollectionOfComplexWithMetadataNone() throws Exception{ - final String METADATA_TEXT = "@odata."; - final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESCompCollComp"); - final EntityCollection entitySet = data.readAll(edmEntitySet); - InputStream result = serializerNoMetadata - .entityCollection(metadata, edmEntitySet.getEntityType(), entitySet, - EntityCollectionSerializerOptions.with() - .contextURL(ContextURL.with().entitySet(edmEntitySet).build()) - .build()) - .getContent(); - final String resultString = IOUtils.toString(result); - Assert.assertEquals(false, resultString.contains(METADATA_TEXT)); - } - + .getContent(); + final String resultString = IOUtils.toString(result); + final String expectedResult = "{\"@odata.context\":\"$metadata#ESCompCollComp\"," + + "\"@odata.metadataEtag\":\"W/\\\"metadataETag\\\"\"," + + "\"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\"" + + "}]}}]}"; + Assert.assertEquals(expectedResult, resultString); + } + + @Test + public void selectComplexNestedCollectionOfComplexWithMetadataNone() throws Exception{ + final String METADATA_TEXT = "@odata."; + final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESCompCollComp"); + final EntityCollection entitySet = data.readAll(edmEntitySet); + InputStream result = serializerNoMetadata + .entityCollection(metadata, edmEntitySet.getEntityType(), entitySet, + EntityCollectionSerializerOptions.with() + .contextURL(ContextURL.with().entitySet(edmEntitySet).build()) + .build()) + .getContent(); + final String resultString = IOUtils.toString(result); + Assert.assertEquals(false, resultString.contains(METADATA_TEXT)); + } + @Test(expected = SerializerException.class) public void selectMissingId() throws Exception { final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESAllPrim"); @@ -1386,27 +1399,27 @@ public class ODataJsonSerializerTest { .expand(expand) .build()).getContent()); Assert.assertEquals("{\"@odata.context\":\"$metadata#ESTwoPrim/$entity\"," - + "\"@odata.metadataEtag\":\"W/\\\"metadataETag\\\"\"," - + "\"PropertyInt16\":-365,\"PropertyString\":\"Test String2\"," - + "\"NavPropertyETAllPrimOne\":null," - + "\"NavPropertyETAllPrimMany\":[" - + "{\"PropertyInt16\":-32768,\"PropertyString\":\"Second Resource - negative values\"," - + "\"PropertyBoolean\":false,\"PropertyByte\":0,\"PropertySByte\":-128,\"PropertyInt32\":-2147483648," - + "\"PropertyInt64\":-9223372036854775808,\"PropertySingle\":-1.79E8,\"PropertyDouble\":-179000.0," - + "\"PropertyDecimal\":-34,\"PropertyBinary\":\"ASNFZ4mrze8=\",\"PropertyDate\":\"2015-11-05\"," - + "\"PropertyDateTimeOffset\":\"2005-12-03T07:17:08Z\",\"PropertyDuration\":\"PT9S\"," - + "\"PropertyGuid\":\"76543201-23ab-cdef-0123-456789dddfff\",\"PropertyTimeOfDay\":\"23:49:14\"," - + "\"NavPropertyETTwoPrimOne\":null,\"NavPropertyETTwoPrimMany\":[]}," - + "{\"PropertyInt16\":0,\"PropertyString\":\"\",\"PropertyBoolean\":false,\"PropertyByte\":0," - + "\"PropertySByte\":0,\"PropertyInt32\":0,\"PropertyInt64\":0,\"PropertySingle\":0.0," - + "\"PropertyDouble\":0.0,\"PropertyDecimal\":0,\"PropertyBinary\":\"\"," - + "\"PropertyDate\":\"1970-01-01\",\"PropertyDateTimeOffset\":\"2005-12-03T00:00:00Z\"," - + "\"PropertyDuration\":\"PT0S\",\"PropertyGuid\":\"76543201-23ab-cdef-0123-456789cccddd\"," - + "\"PropertyTimeOfDay\":\"00:01:01\",\"NavPropertyETTwoPrimOne\":null," - + "\"NavPropertyETTwoPrimMany\":[" - + "{\"PropertyInt16\":32766,\"PropertyString\":\"Test String1\"}," - + "{\"PropertyInt16\":-32766,\"PropertyString\":null}," - + "{\"PropertyInt16\":32767,\"PropertyString\":\"Test String4\"}]}]}", + + "\"@odata.metadataEtag\":\"W/\\\"metadataETag\\\"\"," + + "\"PropertyInt16\":-365,\"PropertyString\":\"Test String2\"," + + "\"NavPropertyETAllPrimOne\":null," + + "\"NavPropertyETAllPrimMany\":[" + + "{\"PropertyInt16\":-32768,\"PropertyString\":\"Second Resource - negative values\"," + + "\"PropertyBoolean\":false,\"PropertyByte\":0,\"PropertySByte\":-128,\"PropertyInt32\":-2147483648," + + "\"PropertyInt64\":-9223372036854775808,\"PropertySingle\":-1.79E8,\"PropertyDouble\":-179000.0," + + "\"PropertyDecimal\":-34,\"PropertyBinary\":\"ASNFZ4mrze8=\",\"PropertyDate\":\"2015-11-05\"," + + "\"PropertyDateTimeOffset\":\"2005-12-03T07:17:08Z\",\"PropertyDuration\":\"PT9S\"," + + "\"PropertyGuid\":\"76543201-23ab-cdef-0123-456789dddfff\",\"PropertyTimeOfDay\":\"23:49:14\"," + + "\"NavPropertyETTwoPrimOne\":null,\"NavPropertyETTwoPrimMany\":[]}," + + "{\"PropertyInt16\":0,\"PropertyString\":\"\",\"PropertyBoolean\":false,\"PropertyByte\":0," + + "\"PropertySByte\":0,\"PropertyInt32\":0,\"PropertyInt64\":0,\"PropertySingle\":0.0," + + "\"PropertyDouble\":0.0,\"PropertyDecimal\":0,\"PropertyBinary\":\"\"," + + "\"PropertyDate\":\"1970-01-01\",\"PropertyDateTimeOffset\":\"2005-12-03T00:00:00Z\"," + + "\"PropertyDuration\":\"PT0S\",\"PropertyGuid\":\"76543201-23ab-cdef-0123-456789cccddd\"," + + "\"PropertyTimeOfDay\":\"00:01:01\",\"NavPropertyETTwoPrimOne\":null," + + "\"NavPropertyETTwoPrimMany\":[" + + "{\"PropertyInt16\":32766,\"PropertyString\":\"Test String1\"}," + + "{\"PropertyInt16\":-32766,\"PropertyString\":null}," + + "{\"PropertyInt16\":32767,\"PropertyString\":\"Test String4\"}]}]}", resultString); } @@ -1908,24 +1921,182 @@ public class ODataJsonSerializerTest { } Assert.assertEquals(3, count); } - - @Test - public void expandCycle() throws Exception { - final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESPeople"); - final Entity entity = data.readAll(edmEntitySet).getEntities().get(1); - ExpandItem mockExpandItem = ExpandSelectMock.mockExpandItem(edmEntitySet, "friends"); - LevelsExpandOption levels = Mockito.mock(LevelsExpandOption.class); - Mockito.when(levels.isMax()).thenReturn(Boolean.TRUE); - Mockito.when(mockExpandItem.getLevelsOption()).thenReturn(levels); - final ExpandOption expand = ExpandSelectMock.mockExpandOption(Collections.singletonList( - mockExpandItem)); - InputStream result = serializer.entity(metadata, edmEntitySet.getEntityType(), entity, - EntitySerializerOptions.with() - .contextURL(ContextURL.with().entitySet(edmEntitySet).suffix(Suffix.ENTITY).build()) - .expand(expand) - .build()).getContent(); - final String resultString = IOUtils.toString(result); - String expected = "{" + + + @Test + public void geoPoint() throws Exception { + final EdmEntityType entityType = mockEntityType(EdmPrimitiveTypeKind.GeometryPoint); + Entity entity = new Entity() + .addProperty(new Property(null, entityType.getPropertyNames().get(0), ValueType.GEOSPATIAL, + createPoint(1.5, 4.25))); + Assert.assertEquals("{\"" + entityType.getPropertyNames().get(0) + "\":{" + + "\"type\":\"Point\",\"coordinates\":[1.5,4.25]}}", + IOUtils.toString(serializerNoMetadata.entity(metadata, entityType, entity, null).getContent())); + + Point point = new Point(Dimension.GEOMETRY, null); + point.setZ(42); + entity = new Entity().addProperty(new Property(null, entityType.getPropertyNames().get(0), ValueType.GEOSPATIAL, + point)); + Assert.assertEquals("{\"" + entityType.getPropertyNames().get(0) + "\":{" + + "\"type\":\"Point\",\"coordinates\":[0.0,0.0,42.0]}}", + IOUtils.toString(serializerNoMetadata.entity(metadata, entityType, entity, null).getContent())); + } + + @Test + public void geoMultiPoint() throws Exception { + final EdmEntityType entityType = mockEntityType(EdmPrimitiveTypeKind.GeometryMultiPoint); + final Entity entity = new Entity() + .addProperty(new Property(null, entityType.getPropertyNames().get(0), ValueType.GEOSPATIAL, + new MultiPoint(Dimension.GEOMETRY, null, Arrays.asList( + createPoint(2.5, 3.125), createPoint(3.5, 4.125), createPoint(4.5, 5.125))))); + Assert.assertEquals("{\"" + entityType.getPropertyNames().get(0) + "\":{" + + "\"type\":\"MultiPoint\",\"coordinates\":[[2.5,3.125],[3.5,4.125],[4.5,5.125]]}}", + IOUtils.toString(serializerNoMetadata.entity(metadata, entityType, entity, null).getContent())); + } + + @Test + public void geoLineString() throws Exception { + final EdmEntityType entityType = mockEntityType(EdmPrimitiveTypeKind.GeometryLineString); + final Entity entity = new Entity() + .addProperty(new Property(null, entityType.getPropertyNames().get(0), ValueType.GEOSPATIAL, + new LineString(Dimension.GEOMETRY, null, Arrays.asList( + createPoint(1, 1), createPoint(2, 2), createPoint(3, 3), createPoint(4, 4), createPoint(5, 5))))); + Assert.assertEquals("{\"" + entityType.getPropertyNames().get(0) + "\":{" + + "\"type\":\"LineString\",\"coordinates\":[[1.0,1.0],[2.0,2.0],[3.0,3.0],[4.0,4.0],[5.0,5.0]]}}", + IOUtils.toString(serializerNoMetadata.entity(metadata, entityType, entity, null).getContent())); + } + + @Test + public void geoMultiLineString() throws Exception { + final EdmEntityType entityType = mockEntityType(EdmPrimitiveTypeKind.GeometryMultiLineString); + final Entity entity = new Entity() + .addProperty(new Property(null, entityType.getPropertyNames().get(0), ValueType.GEOSPATIAL, + new MultiLineString(Dimension.GEOMETRY, null, Arrays.asList( + new LineString(Dimension.GEOMETRY, null, Arrays.asList( + createPoint(1, 1), createPoint(2, 2), createPoint(3, 3), createPoint(4, 4), createPoint(5, 5))), + new LineString(Dimension.GEOMETRY, null, Arrays.asList( + createPoint(99.5, 101.5), createPoint(150, 151.25))))))); + Assert.assertEquals("{\"" + entityType.getPropertyNames().get(0) + "\":{" + + "\"type\":\"MultiLineString\",\"coordinates\":[" + + "[[1.0,1.0],[2.0,2.0],[3.0,3.0],[4.0,4.0],[5.0,5.0]]," + + "[[99.5,101.5],[150.0,151.25]]]}}", + IOUtils.toString(serializerNoMetadata.entity(metadata, entityType, entity, null).getContent())); + } + + @Test + public void geoPolygon() throws Exception { + final EdmEntityType entityType = mockEntityType(EdmPrimitiveTypeKind.GeometryPolygon); + Entity entity = new Entity() + .addProperty(new Property(null, entityType.getPropertyNames().get(0), ValueType.GEOSPATIAL, + new Polygon(Dimension.GEOMETRY, null, + Arrays.asList( + createPoint(1, 1), createPoint(1, 2), createPoint(2, 2), createPoint(2, 1), createPoint(1, 1)), + Arrays.asList( + createPoint(0, 0), createPoint(3, 0), createPoint(3, 3), createPoint(0, 3), createPoint(0, 0))))); + Assert.assertEquals("{\"" + entityType.getPropertyNames().get(0) + "\":{" + + "\"type\":\"Polygon\",\"coordinates\":[[[0.0,0.0],[3.0,0.0],[3.0,3.0],[0.0,3.0],[0.0,0.0]]," + + "[[1.0,1.0],[1.0,2.0],[2.0,2.0],[2.0,1.0],[1.0,1.0]]]}}", + IOUtils.toString(serializerNoMetadata.entity(metadata, entityType, entity, null).getContent())); + + entity = new Entity().addProperty(new Property(null, entityType.getPropertyNames().get(0), ValueType.GEOSPATIAL, + new Polygon(Dimension.GEOMETRY, null, null, Arrays.asList( + createPoint(10, 10), createPoint(30, 10), createPoint(30, 30), createPoint(10, 30), + createPoint(10, 10))))); + Assert.assertEquals("{\"" + entityType.getPropertyNames().get(0) + "\":{" + + "\"type\":\"Polygon\",\"coordinates\":[[[10.0,10.0],[30.0,10.0],[30.0,30.0],[10.0,30.0],[10.0,10.0]]]}}", + IOUtils.toString(serializerNoMetadata.entity(metadata, entityType, entity, null).getContent())); + } + + @Test + public void geoMultiPolygon() throws Exception { + final EdmEntityType entityType = mockEntityType(EdmPrimitiveTypeKind.GeometryMultiPolygon); + final Entity entity = new Entity() + .addProperty(new Property(null, entityType.getPropertyNames().get(0), ValueType.GEOSPATIAL, + new MultiPolygon(Dimension.GEOMETRY, null, Arrays.asList( + new Polygon(Dimension.GEOMETRY, null, + Arrays.asList( + createPoint(1, 1), createPoint(1, 2), createPoint(2, 2), createPoint(2, 1), createPoint(1, 1)), + Arrays.asList( + createPoint(0, 0), createPoint(3, 0), createPoint(3, 3), createPoint(0, 3), + createPoint(0, 0))), + new Polygon(Dimension.GEOMETRY, null, + Arrays.asList( + createPoint(10, 10), createPoint(10, 20), createPoint(20, 10), createPoint(10, 10)), + Arrays.asList( + createPoint(0, 0), createPoint(30, 0), createPoint(0, 30), createPoint(0, 0))))))); + Assert.assertEquals("{\"" + entityType.getPropertyNames().get(0) + "\":{" + + "\"type\":\"MultiPolygon\",\"coordinates\":[" + + "[[[0.0,0.0],[3.0,0.0],[3.0,3.0],[0.0,3.0],[0.0,0.0]]," + + "[[1.0,1.0],[1.0,2.0],[2.0,2.0],[2.0,1.0],[1.0,1.0]]]," + + "[[[0.0,0.0],[30.0,0.0],[0.0,30.0],[0.0,0.0]]," + + "[[10.0,10.0],[10.0,20.0],[20.0,10.0],[10.0,10.0]]]]}}", + IOUtils.toString(serializerNoMetadata.entity(metadata, entityType, entity, null).getContent())); + } + + @Test + public void geoCollection() throws Exception { + final EdmEntityType entityType = mockEntityType(EdmPrimitiveTypeKind.GeometryCollection); + final Entity entity = new Entity() + .addProperty(new Property(null, entityType.getPropertyNames().get(0), ValueType.GEOSPATIAL, + new GeospatialCollection(Dimension.GEOMETRY, null, Arrays.asList( + createPoint(100, 0), + new LineString(Dimension.GEOMETRY, null, Arrays.asList(createPoint(101, 0), createPoint(102, 1))))))); + Assert.assertEquals("{\"" + entityType.getPropertyNames().get(0) + "\":{" + + "\"type\":\"GeometryCollection\",\"geometries\":[" + + "{\"type\":\"Point\",\"coordinates\":[100.0,0.0]}," + + "{\"type\":\"LineString\",\"coordinates\":[[101.0,0.0],[102.0,1.0]]}]}}", + IOUtils.toString(serializerNoMetadata.entity(metadata, entityType, entity, null).getContent())); + } + + private EdmEntityType mockEntityType(final EdmPrimitiveTypeKind type) { + EdmProperty property = Mockito.mock(EdmProperty.class); + final String name = "Property" + type.name(); + Mockito.when(property.getName()).thenReturn(name); + Mockito.when(property.getType()).thenReturn(odata.createPrimitiveTypeInstance(type)); + Mockito.when(property.isPrimitive()).thenReturn(true); + EdmEntityType entityType = Mockito.mock(EdmEntityType.class); + Mockito.when(entityType.getPropertyNames()).thenReturn(Arrays.asList(name)); + Mockito.when(entityType.getStructuralProperty(name)).thenReturn(property); + return entityType; + } + + @Test + public void geoNonstandardSRID() throws Exception { + final EdmEntityType entityType = mockEntityType(EdmPrimitiveTypeKind.GeometryPoint); + final Entity entity = new Entity() + .addProperty(new Property(null, entityType.getPropertyNames().get(0), ValueType.GEOSPATIAL, + new Point(Dimension.GEOMETRY, SRID.valueOf("42")))); + try { + serializerNoMetadata.entity(metadata, entityType, entity, null); + fail("Expected exception not thrown."); + } catch (final SerializerException e) { + assertNotNull(e); + } + } + + private Point createPoint(final double x, final double y) { + Point point = new Point(Dimension.GEOMETRY, null); + point.setX(x); + point.setY(y); + return point; + } + + @Test + public void expandCycle() throws Exception { + final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESPeople"); + final Entity entity = data.readAll(edmEntitySet).getEntities().get(1); + ExpandItem mockExpandItem = ExpandSelectMock.mockExpandItem(edmEntitySet, "friends"); + LevelsExpandOption levels = Mockito.mock(LevelsExpandOption.class); + Mockito.when(levels.isMax()).thenReturn(Boolean.TRUE); + Mockito.when(mockExpandItem.getLevelsOption()).thenReturn(levels); + final ExpandOption expand = ExpandSelectMock.mockExpandOption(Collections.singletonList(mockExpandItem)); + InputStream result = serializer.entity(metadata, edmEntitySet.getEntityType(), entity, + EntitySerializerOptions.with() + .contextURL(ContextURL.with().entitySet(edmEntitySet).suffix(Suffix.ENTITY).build()) + .expand(expand) + .build()) + .getContent(); + final String resultString = IOUtils.toString(result); + final String expected = "{" + "\"@odata.context\":\"$metadata#ESPeople/$entity\"," + "\"@odata.metadataEtag\":\"W/\\\"metadataETag\\\"\"," + "\"id\":1," + @@ -1981,27 +2152,27 @@ public class ODataJsonSerializerTest { "}" + "]" + "}"; - Assert.assertEquals(expected, resultString); - } - - @Test - public void expandCycleWith3Level() throws Exception { - final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESPeople"); - final Entity entity = data.readAll(edmEntitySet).getEntities().get(1); - ExpandItem mockExpandItem = ExpandSelectMock.mockExpandItem(edmEntitySet, "friends"); - LevelsExpandOption levels = Mockito.mock(LevelsExpandOption.class); - Mockito.when(levels.isMax()).thenReturn(Boolean.FALSE); - Mockito.when(levels.getValue()).thenReturn(3); - Mockito.when(mockExpandItem.getLevelsOption()).thenReturn(levels); - final ExpandOption expand = ExpandSelectMock.mockExpandOption(Collections.singletonList( - mockExpandItem)); - InputStream result = serializer.entity(metadata, edmEntitySet.getEntityType(), entity, - EntitySerializerOptions.with() - .contextURL(ContextURL.with().entitySet(edmEntitySet).suffix(Suffix.ENTITY).build()) - .expand(expand) - .build()).getContent(); - final String resultString = IOUtils.toString(result); - String expected = "{" + + Assert.assertEquals(expected, resultString); + } + + @Test + public void expandCycleWith3Level() throws Exception { + final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESPeople"); + final Entity entity = data.readAll(edmEntitySet).getEntities().get(1); + ExpandItem mockExpandItem = ExpandSelectMock.mockExpandItem(edmEntitySet, "friends"); + LevelsExpandOption levels = Mockito.mock(LevelsExpandOption.class); + Mockito.when(levels.isMax()).thenReturn(Boolean.FALSE); + Mockito.when(levels.getValue()).thenReturn(3); + Mockito.when(mockExpandItem.getLevelsOption()).thenReturn(levels); + final ExpandOption expand = ExpandSelectMock.mockExpandOption(Collections.singletonList(mockExpandItem)); + InputStream result = serializer.entity(metadata, edmEntitySet.getEntityType(), entity, + EntitySerializerOptions.with() + .contextURL(ContextURL.with().entitySet(edmEntitySet).suffix(Suffix.ENTITY).build()) + .expand(expand) + .build()) + .getContent(); + final String resultString = IOUtils.toString(result); + final String expected = "{" + "\"@odata.context\":\"$metadata#ESPeople/$entity\"," + "\"@odata.metadataEtag\":\"W/\\\"metadataETag\\\"\"," + "\"id\":1," + @@ -2055,6 +2226,6 @@ public class ODataJsonSerializerTest { "}" + "]" + "}"; - Assert.assertEquals(expected, resultString); - } + Assert.assertEquals(expected, resultString); + } }