[OLINGO-935] new server serializer for dynamic data

Signed-off-by: Christian Amend <[email protected]>


Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo
Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/bc61da3d
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/bc61da3d
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/bc61da3d

Branch: refs/heads/master
Commit: bc61da3dfd753ff5ac03b1686a73b7bd3c23f36d
Parents: 61b2f72
Author: Klaus Straubinger <[email protected]>
Authored: Fri Jul 22 13:40:12 2016 +0200
Committer: Christian Amend <[email protected]>
Committed: Mon Jul 25 11:10:40 2016 +0200

----------------------------------------------------------------------
 .../json/EdmAssistedJsonSerializer.java         | 406 +++++++++++++++++++
 .../json/EdmAssistedJsonSerializerTest.java     | 382 +++++++++++++++++
 2 files changed, 788 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/bc61da3d/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/EdmAssistedJsonSerializer.java
----------------------------------------------------------------------
diff --git 
a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/EdmAssistedJsonSerializer.java
 
b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/EdmAssistedJsonSerializer.java
new file mode 100644
index 0000000..79d221b
--- /dev/null
+++ 
b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/EdmAssistedJsonSerializer.java
@@ -0,0 +1,406 @@
+/*
+ * 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.serializer.json;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.URI;
+import java.util.List;
+
+import org.apache.olingo.commons.api.Constants;
+import org.apache.olingo.commons.api.data.AbstractEntityCollection;
+import org.apache.olingo.commons.api.data.AbstractODataObject;
+import org.apache.olingo.commons.api.data.Annotatable;
+import org.apache.olingo.commons.api.data.Annotation;
+import org.apache.olingo.commons.api.data.ComplexValue;
+import org.apache.olingo.commons.api.data.ContextURL;
+import org.apache.olingo.commons.api.data.Entity;
+import org.apache.olingo.commons.api.data.Link;
+import org.apache.olingo.commons.api.data.Linked;
+import org.apache.olingo.commons.api.data.Property;
+import org.apache.olingo.commons.api.data.Valuable;
+import org.apache.olingo.commons.api.data.ValueType;
+import org.apache.olingo.commons.api.edm.EdmComplexType;
+import org.apache.olingo.commons.api.edm.EdmEntityType;
+import org.apache.olingo.commons.api.edm.EdmPrimitiveType;
+import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException;
+import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
+import org.apache.olingo.commons.api.edm.EdmProperty;
+import org.apache.olingo.commons.api.edm.EdmType;
+import org.apache.olingo.commons.api.format.ContentType;
+import org.apache.olingo.commons.core.edm.EdmTypeInfo;
+import 
org.apache.olingo.commons.core.edm.primitivetype.EdmPrimitiveTypeFactory;
+import org.apache.olingo.server.api.ServiceMetadata;
+import org.apache.olingo.server.api.serializer.SerializerException;
+import org.apache.olingo.server.api.serializer.SerializerException.MessageKeys;
+import org.apache.olingo.server.api.serializer.SerializerResult;
+import org.apache.olingo.server.core.serializer.SerializerResultImpl;
+import org.apache.olingo.server.core.serializer.utils.CircleStreamBuffer;
+import org.apache.olingo.server.core.serializer.utils.ContentTypeHelper;
+import org.apache.olingo.server.core.serializer.utils.ContextURLBuilder;
+
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonGenerator;
+
+public class EdmAssistedJsonSerializer {
+
+  private static final String IO_EXCEPTION_TEXT = "An I/O exception occurred.";
+
+  protected final boolean isIEEE754Compatible;
+  protected final boolean isODataMetadataNone;
+
+  public EdmAssistedJsonSerializer(final ContentType contentType) {
+    this.isIEEE754Compatible = 
ContentTypeHelper.isODataIEEE754Compatible(contentType);
+    this.isODataMetadataNone = 
ContentTypeHelper.isODataMetadataNone(contentType);
+  }
+
+  public SerializerResult entityCollection(final ServiceMetadata metadata, 
final EdmEntityType entityType,
+      final AbstractEntityCollection entityCollection, final ContextURL 
contextURL) throws SerializerException {
+    return serialize(metadata, entityType, entityCollection, contextURL);
+  }
+
+  public SerializerResult entity(final ServiceMetadata metadata, final 
EdmEntityType entityType, final Entity entity,
+      final ContextURL contextURL) throws SerializerException {
+    return serialize(metadata, entityType, entity, contextURL);
+  }
+
+  protected SerializerResult serialize(final ServiceMetadata metadata, final 
EdmEntityType entityType, final AbstractODataObject obj,
+      final ContextURL contextURL) throws SerializerException {
+    final String metadataETag =
+        isODataMetadataNone || metadata == null || 
metadata.getServiceMetadataETagSupport() == null ?
+            null :
+            metadata.getServiceMetadataETagSupport().getMetadataETag();
+    final String contextURLString = isODataMetadataNone || contextURL == null ?
+        null :
+        ContextURLBuilder.create(contextURL).toASCIIString();
+    OutputStream outputStream = null;
+    SerializerException cachedException = null;
+    try {
+      CircleStreamBuffer buffer = new CircleStreamBuffer();
+      outputStream = buffer.getOutputStream();
+      JsonGenerator json = new JsonFactory().createGenerator(outputStream);
+      if (obj instanceof AbstractEntityCollection) {
+        doSerialize(entityType, (AbstractEntityCollection) obj, 
contextURLString, metadataETag, json);
+      } else if (obj instanceof Entity) {
+        doSerialize(entityType, (Entity) obj, contextURLString, metadataETag, 
json);
+      } else {
+        throw new SerializerException("Input type not supported.", 
MessageKeys.NOT_IMPLEMENTED);
+      }
+      json.flush();
+      json.close();
+      outputStream.close();
+      return 
SerializerResultImpl.with().content(buffer.getInputStream()).build();
+    } catch (final IOException e) {
+      cachedException = new SerializerException(IO_EXCEPTION_TEXT, e, 
SerializerException.MessageKeys.IO_EXCEPTION);
+      throw cachedException;
+    } finally {
+      if (outputStream != null) {
+        try {
+          outputStream.close();
+        } catch (final IOException e) {
+          throw cachedException == null ?
+              new SerializerException(IO_EXCEPTION_TEXT, e, 
SerializerException.MessageKeys.IO_EXCEPTION) :
+              cachedException;
+        }
+      }
+    }
+  }
+
+  protected void doSerialize(final EdmEntityType entityType, final 
AbstractEntityCollection entityCollection,
+      final String contextURLString, final String metadataETag, JsonGenerator 
json)
+      throws IOException, SerializerException {
+
+    json.writeStartObject();
+
+    metadata(contextURLString, metadataETag, null, null, 
entityCollection.getId(), false, json);
+
+    if (entityCollection.getCount() != null) {
+      if (isIEEE754Compatible) {
+        json.writeStringField(Constants.JSON_COUNT, 
Integer.toString(entityCollection.getCount()));
+      } else {
+        json.writeNumberField(Constants.JSON_COUNT, 
entityCollection.getCount());
+      }
+    }
+    if (entityCollection.getDeltaLink() != null) {
+      json.writeStringField(Constants.JSON_DELTA_LINK, 
entityCollection.getDeltaLink().toASCIIString());
+    }
+
+    for (final Annotation annotation : entityCollection.getAnnotations()) {
+      valuable(json, annotation, '@' + annotation.getTerm(), null, null);
+    }
+
+    json.writeArrayFieldStart(Constants.VALUE);
+    for (final Entity entity : entityCollection) {
+      doSerialize(entityType, entity, null, null, json);
+    }
+    json.writeEndArray();
+
+    if (entityCollection.getNext() != null) {
+      json.writeStringField(Constants.JSON_NEXT_LINK, 
entityCollection.getNext().toASCIIString());
+    }
+
+    json.writeEndObject();
+  }
+
+  protected void doSerialize(final EdmEntityType entityType, final Entity 
entity,
+      final String contextURLString, final String metadataETag, JsonGenerator 
json)
+      throws IOException, SerializerException {
+
+    json.writeStartObject();
+
+    final String typeName = entity.getType() == null ?
+        null :
+        new 
EdmTypeInfo.Builder().setTypeExpression(entity.getType()).build().external();
+    metadata(contextURLString, metadataETag, entity.getETag(), typeName, 
entity.getId(), true, json);
+
+    for (final Annotation annotation : entity.getAnnotations()) {
+      valuable(json, annotation, '@' + annotation.getTerm(), null, null);
+    }
+
+    for (final Property property : entity.getProperties()) {
+      final String name = property.getName();
+      final EdmProperty edmProperty = entityType == null || 
entityType.getStructuralProperty(name) == null ?
+          null :
+          entityType.getStructuralProperty(name);
+      valuable(json, property, name, edmProperty == null ? null : 
edmProperty.getType(), edmProperty);
+    }
+
+    if (!isODataMetadataNone) {
+      if (entity.getEditLink() != null && entity.getEditLink().getHref() != 
null) {
+        json.writeStringField(Constants.JSON_EDIT_LINK, 
entity.getEditLink().getHref());
+
+        if (entity.isMediaEntity()) {
+          json.writeStringField(Constants.JSON_MEDIA_READ_LINK, 
entity.getEditLink().getHref() + "/$value");
+        }
+      }
+    }
+
+    links(entity, entityType, json);
+
+    json.writeEndObject();
+  }
+
+  private void metadata(final String contextURLString, final String 
metadataETag, final String eTag,
+      final String type, final URI id, final boolean writeNullId, 
JsonGenerator json)
+      throws IOException, SerializerException {
+    if (!isODataMetadataNone) {
+      if (contextURLString != null) {
+        json.writeStringField(Constants.JSON_CONTEXT, contextURLString);
+      }
+      if (metadataETag != null) {
+        json.writeStringField(Constants.JSON_METADATA_ETAG, metadataETag);
+      }
+      if (eTag != null) {
+        json.writeStringField(Constants.JSON_ETAG, eTag);
+      }
+      if (type != null) {
+        json.writeStringField(Constants.JSON_TYPE, type);
+      }
+      if (id == null) {
+        if (writeNullId) {
+          json.writeNullField(Constants.JSON_ID);
+        }
+      } else {
+        json.writeStringField(Constants.JSON_ID, id.toASCIIString());
+      }
+    }
+  }
+
+  private void links(final Linked linked, final EdmEntityType entityType, 
JsonGenerator json)
+      throws IOException, SerializerException {
+
+    for (final Link link : linked.getNavigationLinks()) {
+      final String name = link.getTitle();
+      for (final Annotation annotation : link.getAnnotations()) {
+        valuable(json, annotation, name + '@' + annotation.getTerm(), null, 
null);
+      }
+
+      final EdmEntityType targetType =
+          entityType == null || name == null || 
entityType.getNavigationProperty(name) == null ?
+              null :
+              entityType.getNavigationProperty(name).getType();
+      if (link.getInlineEntity() != null) {
+        json.writeFieldName(name);
+        doSerialize(targetType, link.getInlineEntity(), null, null, json);
+      } else if (link.getInlineEntitySet() != null) {
+        json.writeArrayFieldStart(name);
+        for (final Entity subEntry : link.getInlineEntitySet().getEntities()) {
+          doSerialize(targetType, subEntry, null, null, json);
+        }
+        json.writeEndArray();
+      }
+    }
+  }
+
+  private void collection(final JsonGenerator json, final EdmType itemType, 
final String typeName,
+      final EdmProperty edmProperty, final ValueType valueType, final List<?> 
value)
+      throws IOException, SerializerException {
+
+    json.writeStartArray();
+
+    for (final Object item : value) {
+      switch (valueType) {
+      case COLLECTION_PRIMITIVE:
+        primitiveValue(json, (EdmPrimitiveType) itemType, typeName, 
edmProperty, item);
+        break;
+
+      case COLLECTION_GEOSPATIAL:
+      case COLLECTION_ENUM:
+        throw new SerializerException("Geo and enum types are not supported.", 
MessageKeys.NOT_IMPLEMENTED);
+
+      case COLLECTION_COMPLEX:
+        complexValue(json, (EdmComplexType) itemType, typeName, (ComplexValue) 
item);
+        break;
+
+      default:
+      }
+    }
+
+    json.writeEndArray();
+  }
+
+  protected void primitiveValue(final JsonGenerator json, final 
EdmPrimitiveType valueType, final String typeName,
+      final EdmProperty edmProperty, final Object value) throws IOException, 
SerializerException {
+
+    EdmPrimitiveType type = valueType;
+    if (type == null) {
+      final EdmPrimitiveTypeKind kind =
+          typeName == null ?
+              EdmTypeInfo.determineTypeKind(value) :
+              new 
EdmTypeInfo.Builder().setTypeExpression(typeName).build().getPrimitiveTypeKind();
+      type = kind == null ? null : EdmPrimitiveTypeFactory.getInstance(kind);
+    }
+
+    if (value == null) {
+      json.writeNull();
+    } else if (type == null) {
+      throw new SerializerException("The primitive type could not be 
determined.",
+          MessageKeys.INCONSISTENT_PROPERTY_TYPE, "");
+    } else if (type == 
EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Boolean)) {
+      json.writeBoolean((Boolean) value);
+    } else {
+      String serialized = null;
+      try {
+        serialized = type.valueToString(value,
+            edmProperty == null ? null : edmProperty.isNullable(),
+            edmProperty == null ? null : edmProperty.getMaxLength(),
+            edmProperty == null ? Constants.DEFAULT_PRECISION : 
edmProperty.getPrecision(),
+            edmProperty == null ? Constants.DEFAULT_SCALE : 
edmProperty.getScale(),
+            edmProperty == null ? null : edmProperty.isUnicode());
+      } catch (final EdmPrimitiveTypeException e) {
+        final String name = edmProperty == null ? "" : edmProperty.getName();
+        throw new SerializerException("Wrong value for property '" + name + 
"'!", e,
+            SerializerException.MessageKeys.WRONG_PROPERTY_VALUE, name, 
value.toString());
+      }
+      if (isIEEE754Compatible &&
+          (type == 
EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Int64)
+              || type == 
EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal))
+          || type != 
EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Byte)
+          && type != 
EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.SByte)
+          && type != 
EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Single)
+          && type != 
EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Double)
+          && type != 
EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Int16)
+          && type != 
EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Int32)
+          && type != 
EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Int64)
+          && type != 
EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal)) {
+        json.writeString(serialized);
+      } else {
+        json.writeNumber(serialized);
+      }
+    }
+  }
+
+  private void complexValue(final JsonGenerator json, final EdmComplexType 
valueType, final String typeName,
+      final ComplexValue value) throws IOException, SerializerException {
+    json.writeStartObject();
+
+    if (typeName != null && !isODataMetadataNone) {
+      json.writeStringField(Constants.JSON_TYPE, typeName);
+    }
+
+    for (final Property property : value.getValue()) {
+      final String name = property.getName();
+      final EdmProperty edmProperty = valueType == null || 
valueType.getStructuralProperty(name) == null ?
+          null :
+          valueType.getStructuralProperty(name);
+      valuable(json, property, name, edmProperty == null ? null : 
edmProperty.getType(), edmProperty);
+    }
+    links(value, null, json);
+
+    json.writeEndObject();
+  }
+
+  private void value(final JsonGenerator json, final Valuable value, final 
EdmType type, final EdmProperty edmProperty)
+      throws IOException, SerializerException {
+    final String typeName = value.getType() == null ?
+        null :
+        new 
EdmTypeInfo.Builder().setTypeExpression(value.getType()).build().external();
+
+    if (value.isNull()) {
+      json.writeNull();
+    } else if (value.isCollection()) {
+      collection(json, type, typeName, edmProperty, value.getValueType(), 
value.asCollection());
+    } else if (value.isPrimitive()) {
+      primitiveValue(json, (EdmPrimitiveType) type, typeName, edmProperty, 
value.asPrimitive());
+    } else if (value.isComplex()) {
+      complexValue(json, (EdmComplexType) type, typeName, value.asComplex());
+    } else if (value.isEnum() || value.isGeospatial()) {
+      throw new SerializerException("Geo and enum types are not supported.", 
MessageKeys.NOT_IMPLEMENTED);
+    }
+  }
+
+  protected void valuable(JsonGenerator json, final Valuable valuable, final 
String name, final EdmType type,
+      final EdmProperty edmProperty) throws IOException, SerializerException {
+
+    if (!isODataMetadataNone
+        && !(valuable instanceof Annotation) && !valuable.isComplex()) {
+
+      String typeName = valuable.getType();
+      if (typeName == null && type == null) {
+        if (valuable.isPrimitive()) {
+          if (valuable.isCollection()) {
+            if (!valuable.asCollection().isEmpty()) {
+              final EdmPrimitiveTypeKind kind = 
EdmTypeInfo.determineTypeKind(valuable.asCollection().get(0));
+              if (kind != null) {
+                typeName = "Collection(" + 
kind.getFullQualifiedName().getFullQualifiedNameAsString() + ')';
+              }
+            }
+          } else {
+            final EdmPrimitiveTypeKind kind = 
EdmTypeInfo.determineTypeKind(valuable.asPrimitive());
+            if (kind != null) {
+              typeName = 
kind.getFullQualifiedName().getFullQualifiedNameAsString();
+            }
+          }
+        }
+      }
+      if (typeName != null) {
+        json.writeStringField(
+            name + Constants.JSON_TYPE,
+            new 
EdmTypeInfo.Builder().setTypeExpression(typeName).build().external());
+      }
+    }
+
+    for (final Annotation annotation : ((Annotatable) 
valuable).getAnnotations()) {
+      valuable(json, annotation, name + '@' + annotation.getTerm(), null, 
null);
+    }
+
+    json.writeFieldName(name);
+    value(json, valuable, type, edmProperty);
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/bc61da3d/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/EdmAssistedJsonSerializerTest.java
----------------------------------------------------------------------
diff --git 
a/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/EdmAssistedJsonSerializerTest.java
 
b/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/EdmAssistedJsonSerializerTest.java
new file mode 100644
index 0000000..d6f4e73
--- /dev/null
+++ 
b/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/EdmAssistedJsonSerializerTest.java
@@ -0,0 +1,382 @@
+/*
+ * 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.serializer.json;
+
+import java.math.BigDecimal;
+import java.net.URI;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.TimeZone;
+import java.util.UUID;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.olingo.commons.api.data.ComplexValue;
+import org.apache.olingo.commons.api.data.ContextURL;
+import org.apache.olingo.commons.api.data.ContextURL.Suffix;
+import org.apache.olingo.commons.api.data.Entity;
+import org.apache.olingo.commons.api.data.EntityCollection;
+import org.apache.olingo.commons.api.data.Link;
+import org.apache.olingo.commons.api.data.Property;
+import org.apache.olingo.commons.api.data.ValueType;
+import org.apache.olingo.commons.api.edm.EdmEntityContainer;
+import org.apache.olingo.commons.api.edm.EdmEntitySet;
+import org.apache.olingo.commons.api.edmx.EdmxReference;
+import org.apache.olingo.commons.api.format.ContentType;
+import org.apache.olingo.server.api.OData;
+import org.apache.olingo.server.api.ServiceMetadata;
+import org.apache.olingo.server.api.serializer.SerializerException;
+import org.apache.olingo.server.tecsvc.MetadataETagSupport;
+import org.apache.olingo.server.tecsvc.provider.EdmTechProvider;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class EdmAssistedJsonSerializerTest {
+  private static final OData oData = OData.newInstance();
+  private static final ServiceMetadata metadata = oData.createServiceMetadata(
+      new EdmTechProvider(), Collections.<EdmxReference> emptyList(), null);
+  private static final EdmEntityContainer entityContainer = 
metadata.getEdm().getEntityContainer();
+  private final EdmAssistedJsonSerializer serializer = new 
EdmAssistedJsonSerializer(ContentType.JSON);
+
+  @Test
+  public void entity() throws Exception {
+    Entity entity = new Entity();
+    entity.setId(null);
+    entity.addProperty(new Property(null, "Property1", ValueType.PRIMITIVE, 
1.25F));
+    final String resultString = IOUtils.toString(
+        serializer.entity(metadata, null, entity,
+            
ContextURL.with().entitySetOrSingletonOrType("EntitySet").selectList("Property1")
+                .suffix(Suffix.ENTITY)
+                .build())
+            .getContent());
+    final String expectedResult =
+        "{\"@odata.context\":\"$metadata#EntitySet(Property1)/$entity\","
+        + "\"@odata.id\":null,"
+        + "\"[email protected]\":\"Single\",\"Property1\":1.25"
+        + "}";
+    Assert.assertEquals(expectedResult, resultString);
+  }
+
+  @Test
+  public void entityWithEdm() throws Exception {
+    final EdmEntitySet entitySet = entityContainer.getEntitySet("ESTwoPrim");
+    Entity entity = new Entity();
+    entity.setId(null);
+    entity.addProperty(new Property(null, "PropertyInt16", 
ValueType.PRIMITIVE, (short) 1))
+        .addProperty(new Property(null, "PropertyString", ValueType.PRIMITIVE, 
"test"))
+        .addProperty(new Property(null, "AdditionalProperty", 
ValueType.PRIMITIVE, (byte) 42));
+    final String resultString = IOUtils.toString(
+        serializer.entity(metadata, entitySet.getEntityType(), entity,
+            
ContextURL.with().entitySet(entitySet).suffix(Suffix.ENTITY).build())
+            .getContent());
+    final String expectedResult =
+        "{\"@odata.context\":\"$metadata#ESTwoPrim/$entity\","
+        + "\"@odata.id\":null,"
+        + "\"PropertyInt16\":1,\"PropertyString\":\"test\","
+        + 
"\"[email protected]\":\"SByte\",\"AdditionalProperty\":42"
+        + "}";
+    Assert.assertEquals(expectedResult, resultString);
+  }
+
+  @Test
+  public void entityCollection() throws Exception {
+    Entity entity = new Entity();
+    entity.setId(null);
+    entity.addProperty(new Property(null, "Property0", ValueType.PRIMITIVE, 
null))
+        .addProperty(new Property(null, "Property1", ValueType.PRIMITIVE, 1));
+    Calendar date = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
+    date.clear();
+    date.set(2000, 1, 29);
+    entity.addProperty(new Property("Edm.Date", "Property2", 
ValueType.PRIMITIVE, date))
+        .addProperty(new Property("Edm.DateTimeOffset", "Property3", 
ValueType.PRIMITIVE, date))
+        .addProperty(new Property(null, "Property4", 
ValueType.COLLECTION_PRIMITIVE,
+            Arrays.asList(true, false, null)));
+    EntityCollection entityCollection = new EntityCollection();
+    entityCollection.getEntities().add(entity);
+    entityCollection.setCount(2);
+    entityCollection.setNext(URI.create("nextLink"));
+    final String resultString = IOUtils.toString(
+        serializer.entityCollection(metadata, null, entityCollection,
+            ContextURL.with().entitySetOrSingletonOrType("EntitySet")
+                
.selectList("Property0,Property1,Property2,Property3,Property4")
+                .build())
+            .getContent());
+    final String expectedResult =
+        
"{\"@odata.context\":\"$metadata#EntitySet(Property0,Property1,Property2,Property3,Property4)\","
+        + "\"@odata.count\":2,"
+        + "\"value\":[{\"@odata.id\":null,"
+        + "\"Property0\":null,"
+        + "\"[email protected]\":\"Int32\",\"Property1\":1,"
+        + "\"[email protected]\":\"Date\",\"Property2\":\"2000-02-29\","
+        + 
"\"[email protected]\":\"DateTimeOffset\",\"Property3\":\"2000-02-29T00:00:00Z\","
+        + 
"\"[email protected]\":\"#Collection(Boolean)\",\"Property4\":[true,false,null]"
+        + "}],"
+        + "\"@odata.nextLink\":\"nextLink\""
+        + "}";
+    Assert.assertEquals(expectedResult, resultString);
+  }
+
+  @Test
+  public void entityCollectionIEEE754Compatible() throws Exception {
+    EntityCollection entityCollection = new EntityCollection();
+    entityCollection.getEntities().add(new Entity()
+        .addProperty(new Property(null, "Property1", ValueType.PRIMITIVE, 
Long.MIN_VALUE))
+        .addProperty(new Property(null, "Property2", ValueType.PRIMITIVE, 
BigDecimal.valueOf(Long.MAX_VALUE, 10)))
+        .addProperty(new Property("Edm.Byte", "Property3", 
ValueType.PRIMITIVE, 20)));
+    entityCollection.setCount(3);
+    Assert.assertEquals(
+        
"{\"@odata.context\":\"$metadata#EntitySet(Property1,Property2,Property3)\","
+            + "\"@odata.count\":\"3\","
+            + "\"value\":[{\"@odata.id\":null,"
+            + 
"\"[email protected]\":\"Int64\",\"Property1\":\"-9223372036854775808\","
+            + 
"\"[email protected]\":\"Decimal\",\"Property2\":\"922337203.6854775807\","
+            + "\"[email protected]\":\"Byte\",\"Property3\":20}]}",
+        IOUtils.toString(
+            new EdmAssistedJsonSerializer(
+                ContentType.create(ContentType.JSON, 
ContentType.PARAMETER_IEEE754_COMPATIBLE, "true"))
+                .entityCollection(metadata, null, entityCollection,
+                    ContextURL.with().entitySetOrSingletonOrType("EntitySet")
+                        .selectList("Property1,Property2,Property3")
+                        .build())
+                .getContent()));
+  }
+
+  @Test
+  public void entityCollectionWithComplexProperty() throws Exception {
+    Entity entity = new Entity();
+    entity.setId(null);
+    entity.addProperty(new Property(null, "Property1", ValueType.PRIMITIVE, 
1L));
+    ComplexValue complexValue = new ComplexValue();
+    complexValue.getValue().add(new Property(null, "Inner1", 
ValueType.PRIMITIVE,
+        BigDecimal.TEN.scaleByPowerOfTen(-5)));
+    Calendar time = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
+    time.clear();
+    time.set(Calendar.HOUR_OF_DAY, 13);
+    time.set(Calendar.SECOND, 59);
+    time.set(Calendar.MILLISECOND, 999);
+    complexValue.getValue().add(new Property("Edm.TimeOfDay", "Inner2", 
ValueType.PRIMITIVE, time));
+    entity.addProperty(new Property("Namespace.ComplexType", "Property2", 
ValueType.COMPLEX, complexValue));
+    EntityCollection entityCollection = new EntityCollection();
+    entityCollection.getEntities().add(entity);
+    final String resultString = IOUtils.toString(
+        serializer.entityCollection(metadata, null, entityCollection,
+            
ContextURL.with().entitySetOrSingletonOrType("EntitySet").selectList("Property1,Property2").build())
+            .getContent());
+    final String expectedResult = 
"{\"@odata.context\":\"$metadata#EntitySet(Property1,Property2)\","
+        + "\"value\":[{\"@odata.id\":null,"
+        + "\"[email protected]\":\"Int64\",\"Property1\":1,"
+        + "\"Property2\":{"
+        + "\"@odata.type\":\"#Namespace.ComplexType\","
+        + "\"[email protected]\":\"Decimal\",\"Inner1\":0.00010,"
+        + "\"[email protected]\":\"TimeOfDay\",\"Inner2\":\"13:00:59.999\""
+        + "}}]}";
+    Assert.assertEquals(expectedResult, resultString);
+  }
+
+  @Test
+  public void entityWithComplexCollection() throws Exception {
+    final EdmEntitySet entitySet = 
entityContainer.getEntitySet("ESMixPrimCollComp");
+    ComplexValue complexValue1 = new ComplexValue();
+    complexValue1.getValue().add(new Property(null, "PropertyInt16", 
ValueType.PRIMITIVE, 1));
+    complexValue1.getValue().add(new Property(null, "PropertyString", 
ValueType.PRIMITIVE, "one"));
+    ComplexValue complexValue2 = new ComplexValue();
+    complexValue2.getValue().add(new Property(null, "PropertyInt16", 
ValueType.PRIMITIVE, 2));
+    complexValue2.getValue().add(new Property(null, "PropertyString", 
ValueType.PRIMITIVE, "two"));
+    ComplexValue complexValue3 = new ComplexValue();
+    complexValue3.getValue().add(new Property(null, "PropertyInt16", 
ValueType.PRIMITIVE, 3));
+    complexValue3.getValue().add(new Property(null, "PropertyString", 
ValueType.PRIMITIVE, "three"));
+    final Entity entity = new Entity()
+        .addProperty(new Property(null, "CollPropertyComp", 
ValueType.COLLECTION_COMPLEX,
+            Arrays.asList(complexValue1, complexValue2, complexValue3)));
+    final String resultString = IOUtils.toString(
+        serializer.entity(metadata, entitySet.getEntityType(), entity,
+            
ContextURL.with().entitySet(entitySet).selectList("CollPropertyComp").build())
+            .getContent());
+    final String expectedResult = 
"{\"@odata.context\":\"$metadata#ESMixPrimCollComp(CollPropertyComp)\","
+        + "\"@odata.id\":null,"
+        + "\"CollPropertyComp\":["
+        + "{\"PropertyInt16\":1,\"PropertyString\":\"one\"},"
+        + "{\"PropertyInt16\":2,\"PropertyString\":\"two\"},"
+        + "{\"PropertyInt16\":3,\"PropertyString\":\"three\"}]}";
+    Assert.assertEquals(expectedResult, resultString);
+  }
+
+  @Test
+  public void entityWithEmptyCollection() throws Exception {
+    final EdmEntitySet entitySet = 
entityContainer.getEntitySet("ESMixPrimCollComp");
+    final Entity entity = new Entity()
+        .addProperty(new Property(null, "CollPropertyString", 
ValueType.COLLECTION_PRIMITIVE,
+            Collections.emptyList()));
+    Assert.assertEquals(
+        
"{\"@odata.context\":\"$metadata#ESMixPrimCollComp(CollPropertyString)\","
+            + "\"@odata.id\":null,\"CollPropertyString\":[]}",
+        IOUtils.toString(
+            serializer.entity(metadata, entitySet.getEntityType(), entity,
+                
ContextURL.with().entitySet(entitySet).selectList("CollPropertyString").build())
+                .getContent()));
+  }
+
+  @Test
+  public void expand() throws Exception {
+    final Entity relatedEntity1 = new Entity().addProperty(new Property(null, 
"Related1", ValueType.PRIMITIVE, 1.5));
+    final Entity relatedEntity2 = new Entity().addProperty(new Property(null, 
"Related1", ValueType.PRIMITIVE, 2.75));
+    EntityCollection target = new EntityCollection();
+    target.getEntities().add(relatedEntity1);
+    target.getEntities().add(relatedEntity2);
+    Link link = new Link();
+    link.setTitle("NavigationProperty");
+    link.setInlineEntitySet(target);
+    Entity entity = new Entity();
+    entity.setId(null);
+    entity.addProperty(new Property(null, "Property1", ValueType.PRIMITIVE, 
(short) 1));
+    entity.getNavigationLinks().add(link);
+    EntityCollection entityCollection = new EntityCollection();
+    entityCollection.getEntities().add(entity);
+    final String resultString = IOUtils.toString(
+        serializer.entityCollection(metadata, null, entityCollection,
+            ContextURL.with().entitySetOrSingletonOrType("EntitySet")
+                .selectList("Property1,NavigationProperty(Related1)").build())
+            .getContent());
+    final String expectedResult =
+        
"{\"@odata.context\":\"$metadata#EntitySet(Property1,NavigationProperty(Related1))\","
+        + "\"value\":[{\"@odata.id\":null,"
+        + "\"[email protected]\":\"Int16\",\"Property1\":1,"
+        + "\"NavigationProperty\":["
+        + 
"{\"@odata.id\":null,\"[email protected]\":\"Double\",\"Related1\":1.5},"
+        + 
"{\"@odata.id\":null,\"[email protected]\":\"Double\",\"Related1\":2.75}"
+        + "]}]}";
+    Assert.assertEquals(expectedResult, resultString);
+  }
+
+  @Test
+  public void expandWithEdm() throws Exception {
+    final EdmEntitySet entitySet = entityContainer.getEntitySet("ESTwoPrim");
+    Entity entity = new Entity()
+        .addProperty(new Property(null, "PropertyInt16", ValueType.PRIMITIVE, 
(short) 42))
+        .addProperty(new Property(null, "PropertyString", ValueType.PRIMITIVE, 
"test"));
+    final Entity target = new Entity()
+        .addProperty(new Property(null, "PropertyInt16", ValueType.PRIMITIVE, 
(short) 2))
+        .addProperty(new Property(null, "PropertyByte", ValueType.PRIMITIVE, 
3L));
+    Link link = new Link();
+    link.setTitle("NavPropertyETAllPrimOne");
+    link.setInlineEntity(target);
+    entity.getNavigationLinks().add(link);
+    final String resultString = IOUtils.toString(
+        serializer.entity(metadata, entitySet.getEntityType(), entity,
+            
ContextURL.with().entitySet(entitySet).suffix(Suffix.ENTITY).build())
+            .getContent());
+    final String expectedResult =
+        "{\"@odata.context\":\"$metadata#ESTwoPrim/$entity\","
+        + "\"@odata.id\":null,"
+        + "\"PropertyInt16\":42,\"PropertyString\":\"test\","
+        + 
"\"NavPropertyETAllPrimOne\":{\"@odata.id\":null,\"PropertyInt16\":2,\"PropertyByte\":3}"
+        + "}";
+    Assert.assertEquals(expectedResult, resultString);
+  }
+
+  @Test
+  public void metadata() throws Exception {
+    final ServiceMetadata metadata = oData.createServiceMetadata(null, 
Collections.<EdmxReference> emptyList(),
+        new MetadataETagSupport("W/\"42\""));
+    Entity entity = new Entity();
+    entity.setType("Namespace.EntityType");
+    entity.setId(URI.create("ID"));
+    entity.setETag("W/\"1000\"");
+    Link link = new Link();
+    link.setHref("editLink");
+    entity.setEditLink(link);
+    entity.setMediaContentSource(URI.create("media"));
+    entity.addProperty(new Property(null, "Property1", ValueType.PRIMITIVE,
+        UUID.fromString("12345678-ABCD-1234-CDEF-123456789012")));
+    final ContextURL contextURL = 
ContextURL.with().entitySetOrSingletonOrType("EntitySet").selectList("Property1")
+        .suffix(Suffix.ENTITY).build();
+    final String resultString = IOUtils.toString(serializer.entity(metadata, 
null, entity, contextURL).getContent());
+    final String expectedResult =
+        "{\"@odata.context\":\"$metadata#EntitySet(Property1)/$entity\","
+        + "\"@odata.metadataEtag\":\"W/\\\"42\\\"\","
+        + "\"@odata.etag\":\"W/\\\"1000\\\"\","
+        + "\"@odata.type\":\"#Namespace.EntityType\","
+        + "\"@odata.id\":\"ID\","
+        + 
"\"[email protected]\":\"Guid\",\"Property1\":\"12345678-abcd-1234-cdef-123456789012\","
+        + "\"@odata.editLink\":\"editLink\","
+        + "\"@odata.mediaReadLink\":\"editLink/$value\""
+        + "}";
+    Assert.assertEquals(expectedResult, resultString);
+
+    
Assert.assertEquals("{\"Property1\":\"12345678-abcd-1234-cdef-123456789012\"}",
+        IOUtils.toString(new 
EdmAssistedJsonSerializer(ContentType.JSON_NO_METADATA)
+            .entity(metadata, null, entity, contextURL).getContent()));
+  }
+
+  @Test(expected = SerializerException.class)
+  public void enumType() throws Exception {
+    serializer.entity(metadata, null,
+        new Entity().addProperty(new Property(null, "Property1", 
ValueType.ENUM, 42)), null);
+  }
+
+  @Test(expected = SerializerException.class)
+  public void collectionEnumType() throws Exception {
+    serializer.entity(metadata, null,
+        new Entity().addProperty(new Property(null, "Property1", 
ValueType.COLLECTION_ENUM, Arrays.asList(42))),
+        null);
+  }
+
+  @Test(expected = SerializerException.class)
+  public void geoType() throws Exception {
+    serializer.entity(metadata, null,
+        new Entity().addProperty(new Property(null, "Property1", 
ValueType.GEOSPATIAL, 1)), null);
+  }
+
+  @Test(expected = SerializerException.class)
+  public void unsupportedType() throws Exception {
+    serializer.entity(metadata, null,
+        new Entity().addProperty(new Property(null, "Property1", 
ValueType.PRIMITIVE, TimeZone.getDefault())),
+        null);
+  }
+
+  @Test(expected = SerializerException.class)
+  public void wrongValueForType() throws Exception {
+    serializer.entity(metadata, null,
+        new Entity().addProperty(new Property("Edm.SByte", "Property1", 
ValueType.PRIMITIVE, "-1")),
+        null);
+  }
+
+  @Test(expected = SerializerException.class)
+  public void wrongValueForPropertyFacet() throws Exception {
+    serializer.entity(metadata, 
entityContainer.getEntitySet("ESAllPrim").getEntityType(),
+        new Entity().addProperty(
+            new Property(null, "PropertyDecimal", ValueType.PRIMITIVE, 
BigDecimal.ONE.scaleByPowerOfTen(-11))),
+        null);
+  }
+
+  @Test(expected = SerializerException.class)
+  public void wrongValueForPropertyFacetInComplexProperty() throws Exception {
+    ComplexValue innerComplexValue = new ComplexValue();
+    innerComplexValue.getValue().add(new Property(null, "PropertyDecimal", 
ValueType.PRIMITIVE,
+        BigDecimal.ONE.scaleByPowerOfTen(-6)));
+    ComplexValue complexValue = new ComplexValue();
+    complexValue.getValue().add(new Property(null, "PropertyComp", 
ValueType.COMPLEX,
+        innerComplexValue));
+    serializer.entity(metadata, 
entityContainer.getEntitySet("ESKeyNav").getEntityType(),
+        new Entity().addProperty(
+            new Property(null, "CollPropertyComp", 
ValueType.COLLECTION_COMPLEX,
+                Collections.singletonList(complexValue))),
+        null);
+  }
+}

Reply via email to