diff --git a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BasicITCase.java b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BasicITCase.java index 8cef37db6..87dc3c3e5 100644 --- a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BasicITCase.java +++ b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BasicITCase.java @@ -29,6 +29,7 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.junit.Assume.assumeTrue; +import java.io.InputStream; import java.math.BigDecimal; import java.net.URI; import java.util.Collections; @@ -37,6 +38,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import org.apache.olingo.client.api.EdmEnabledODataClient; import org.apache.olingo.client.api.ODataClient; import org.apache.olingo.client.api.communication.ODataClientErrorException; import org.apache.olingo.client.api.communication.request.cud.ODataDeleteRequest; @@ -72,7 +74,9 @@ import org.apache.olingo.client.api.domain.ClientServiceDocument; import org.apache.olingo.client.api.domain.ClientValue; import org.apache.olingo.client.api.edm.xml.Reference; import org.apache.olingo.client.api.edm.xml.XMLMetadata; +import org.apache.olingo.client.api.serialization.ODataDeserializerException; import org.apache.olingo.client.api.uri.URIBuilder; +import org.apache.olingo.client.core.ODataClientFactory; import org.apache.olingo.client.core.uri.URIUtils; import org.apache.olingo.commons.api.edm.Edm; import org.apache.olingo.commons.api.edm.EdmActionImport; @@ -108,6 +112,9 @@ public class BasicITCase extends AbstractParamTecSvcITCase { private static final String ES_TWO_PRIM = "ESTwoPrim"; private static final String ES_KEY_NAV = "ESKeyNav"; private static final String ES_MIX_PRIM_COLL_COMP = "ESMixPrimCollComp"; + private static final String PROPERTY_COMP_NAV = "CollPropertyCompNav"; + private static final String COL_PROPERTY_COMP = "CollPropertyComp"; + private static final String PROPERTY_COMP_TWO_PRIM = "PropertyCompTwoPrim"; @Test public void readServiceDocument() { @@ -1464,4 +1471,107 @@ public class BasicITCase extends AbstractParamTecSvcITCase { assertEquals(BigDecimal.valueOf(34), response.getBody().getPrimitiveValue().toValue()); } + + + @Test + public void test1Olingo1064() throws ODataDeserializerException { + EdmMetadataRequest request = getClient().getRetrieveRequestFactory().getMetadataRequest(SERVICE_URI); + assertNotNull(request); + setCookieHeader(request); + + ODataRetrieveResponse<Edm> response = request.execute(); + saveCookieHeader(response); + assertEquals(HttpStatusCode.OK.getStatusCode(), response.getStatusCode()); + + Edm edm = response.getBody(); + + EdmEnabledODataClient odataClient = ODataClientFactory.getEdmEnabledClient(SERVICE_URI, edm, null); + final InputStream input = Thread.currentThread().getContextClassLoader(). + getResourceAsStream("ESCompAllPrimWithValueForComplexProperty.json"); + ClientEntity entity = odataClient.getReader().readEntity(input, ContentType.JSON); + assertEquals(HttpStatusCode.OK.getStatusCode(), response.getStatusCode()); + assertNotNull(entity.getProperty(PROPERTY_COMP).getComplexValue()); + assertEquals("olingo.odata.test1.CTAllPrim", entity.getProperty(PROPERTY_COMP).getComplexValue().getTypeName()); + assertEquals(PROPERTY_COMP, entity.getProperty(PROPERTY_COMP).getName()); + assertNull(entity.getProperty(PROPERTY_COMP).getComplexValue().get("PropertyString").getPrimitiveValue()); + assertNull(entity.getProperty(PROPERTY_COMP).getComplexValue().get("PropertyBoolean").getPrimitiveValue()); + } + + @Test + public void test2Olingo1064() throws ODataDeserializerException { + EdmMetadataRequest request = getClient().getRetrieveRequestFactory().getMetadataRequest(SERVICE_URI); + assertNotNull(request); + setCookieHeader(request); + + ODataRetrieveResponse<Edm> response = request.execute(); + saveCookieHeader(response); + assertEquals(HttpStatusCode.OK.getStatusCode(), response.getStatusCode()); + + Edm edm = response.getBody(); + + EdmEnabledODataClient odataClient = ODataClientFactory.getEdmEnabledClient(SERVICE_URI, edm, null); + final InputStream input = Thread.currentThread().getContextClassLoader(). + getResourceAsStream("ESCompAllPrimWithNullValueForComplexProperty.json"); + ClientEntity entity = odataClient.getReader().readEntity(input, ContentType.JSON); + assertEquals(HttpStatusCode.OK.getStatusCode(), response.getStatusCode()); + assertNotNull(entity.getProperty(PROPERTY_COMP).getComplexValue()); + assertEquals("olingo.odata.test1.CTAllPrim", entity.getProperty(PROPERTY_COMP).getComplexValue().getTypeName()); + assertEquals(PROPERTY_COMP, entity.getProperty(PROPERTY_COMP).getName()); + assertNull(entity.getProperty(PROPERTY_COMP).getComplexValue().get(PROPERTY_COMP).getComplexValue()); + } + + @Test + public void test3Olingo1064() throws ODataDeserializerException { + EdmMetadataRequest request = getClient().getRetrieveRequestFactory().getMetadataRequest(SERVICE_URI); + assertNotNull(request); + setCookieHeader(request); + + ODataRetrieveResponse<Edm> response = request.execute(); + saveCookieHeader(response); + assertEquals(HttpStatusCode.OK.getStatusCode(), response.getStatusCode()); + + Edm edm = response.getBody(); + + EdmEnabledODataClient odataClient = ODataClientFactory.getEdmEnabledClient(SERVICE_URI, edm, null); + final InputStream input = Thread.currentThread().getContextClassLoader(). + getResourceAsStream("ESCompAllPrimWithEmptyValueForComplexProperty.json"); + ClientEntity entity = odataClient.getReader().readEntity(input, ContentType.JSON); + assertEquals("olingo.odata.test1.CTAllPrim", entity.getProperty(PROPERTY_COMP).getComplexValue().getTypeName()); + assertEquals(PROPERTY_COMP, entity.getProperty(PROPERTY_COMP).getName()); + assertTrue(entity.getProperty(PROPERTY_COMP).getComplexValue().asJavaMap().size() == 0); + } + + @SuppressWarnings("unchecked") + @Test + public void test4Olingo1064() throws ODataDeserializerException { + EdmMetadataRequest request = getClient().getRetrieveRequestFactory().getMetadataRequest(SERVICE_URI); + assertNotNull(request); + setCookieHeader(request); + + ODataRetrieveResponse<Edm> response = request.execute(); + saveCookieHeader(response); + assertEquals(HttpStatusCode.OK.getStatusCode(), response.getStatusCode()); + + Edm edm = response.getBody(); + + EdmEnabledODataClient odataClient = ODataClientFactory.getEdmEnabledClient(SERVICE_URI, edm, null); + final InputStream input = Thread.currentThread().getContextClassLoader(). + getResourceAsStream("ESTwoKeyNavWithNestedComplexTypes.json"); + ClientEntity entity = odataClient.getReader().readEntity(input, ContentType.JSON); + assertEquals("olingo.odata.test1.CTPrimComp", entity.getProperty(PROPERTY_COMP).getComplexValue().getTypeName()); + assertEquals(PROPERTY_COMP, entity.getProperty(PROPERTY_COMP).getName()); + Map<String, Object> map = entity.getProperty(PROPERTY_COMP).getComplexValue().asJavaMap(); + assertEquals(map.size(), 2); + assertEquals(((Map<String, Object>)map.get(PROPERTY_COMP)).size(), 16); + assertNull(entity.getProperty(PROPERTY_COMP_NAV).getComplexValue().get(PROPERTY_COMP_NAV).getComplexValue()); + assertEquals("Collection(olingo.odata.test1.CTPrimComp)", entity.getProperty(COL_PROPERTY_COMP). + getCollectionValue().getTypeName()); + assertEquals(0, entity.getProperty(COL_PROPERTY_COMP).getCollectionValue().size()); + assertEquals("olingo.odata.test1.CTNavFiveProp", entity.getProperty(PROPERTY_COMP_NAV). + getComplexValue().getTypeName()); + assertEquals("olingo.odata.test1.CTTwoPrim", entity.getProperty(PROPERTY_COMP_TWO_PRIM). + getComplexValue().getTypeName()); + assertNull(entity.getProperty(PROPERTY_COMP_TWO_PRIM).getComplexValue(). + get(PROPERTY_COMP_TWO_PRIM).getComplexValue()); + } } diff --git a/fit/src/test/resources/ESCompAllPrimWithEmptyValueForComplexProperty.json b/fit/src/test/resources/ESCompAllPrimWithEmptyValueForComplexProperty.json new file mode 100644 index 000000000..45bfb75de --- /dev/null +++ b/fit/src/test/resources/ESCompAllPrimWithEmptyValueForComplexProperty.json @@ -0,0 +1,7 @@ +{ + "@odata.context": "$metadata#ESCompAllPrim/$entity", + "@odata.metadataEtag": "W/\"2010a206-ee09-4cfb-9087-df530cdbb6ea\"", + "@odata.etag": "W/\"-32768\"", + "PropertyInt16": -32768, + "PropertyComp": {} +} \ No newline at end of file diff --git a/fit/src/test/resources/ESCompAllPrimWithNullValueForComplexProperty.json b/fit/src/test/resources/ESCompAllPrimWithNullValueForComplexProperty.json new file mode 100644 index 000000000..4d55e067e --- /dev/null +++ b/fit/src/test/resources/ESCompAllPrimWithNullValueForComplexProperty.json @@ -0,0 +1,7 @@ +{ + "@odata.context": "$metadata#ESCompAllPrim/$entity", + "@odata.metadataEtag": "W/\"2010a206-ee09-4cfb-9087-df530cdbb6ea\"", + "@odata.etag": "W/\"-32768\"", + "PropertyInt16": -32768, + "PropertyComp": null +} \ No newline at end of file diff --git a/fit/src/test/resources/ESCompAllPrimWithValueForComplexProperty.json b/fit/src/test/resources/ESCompAllPrimWithValueForComplexProperty.json new file mode 100644 index 000000000..9b09bd001 --- /dev/null +++ b/fit/src/test/resources/ESCompAllPrimWithValueForComplexProperty.json @@ -0,0 +1,7 @@ +{ + "@odata.context": "$metadata#ESCompAllPrim/$entity", + "@odata.metadataEtag": "W/\"2010a206-ee09-4cfb-9087-df530cdbb6ea\"", + "@odata.etag": "W/\"-32768\"", + "PropertyInt16": -32768, + "PropertyComp": { "PropertyString": null, "PropertyBoolean": null } +} \ No newline at end of file diff --git a/fit/src/test/resources/ESTwoKeyNavWithNestedComplexTypes.json b/fit/src/test/resources/ESTwoKeyNavWithNestedComplexTypes.json new file mode 100644 index 000000000..679594d05 --- /dev/null +++ b/fit/src/test/resources/ESTwoKeyNavWithNestedComplexTypes.json @@ -0,0 +1,38 @@ +{ + "@odata.context": "../$metadata#ESTwoKeyNav", + "@odata.metadataEtag": "W/\"e5db7a43-370a-44d7-9600-9f0443e6e351\"", + "PropertyInt16": 1, + "PropertyString": "2", + "PropertyComp": { + "PropertyInt16": 11, + "PropertyComp": { + "PropertyString": "StringValue", + "PropertyBinary": "ASNFZ4mrze8=", + "PropertyBoolean": true, + "PropertyByte": 255, + "PropertyDate": "2012-12-03", + "PropertyDateTimeOffset": null, + "PropertyDecimal": 34, + "PropertySingle": 17900000, + "PropertyDouble": -17900000, + "PropertyDuration": "PT6S", + "PropertyGuid": "01234567-89ab-cdef-0123-456789abcdef", + "PropertyInt16": 32767, + "PropertyInt32": 2147483647, + "PropertyInt64": 9223372036854775807, + "PropertySByte": 127, + "PropertyTimeOfDay": "21:05:59" + } + }, + "PropertyCompNav": { + "PropertyInt16": 1, + "PropertyComp": null + }, + "CollPropertyComp": [], + "CollPropertyCompNav": null, + "CollPropertyString": [ + "1", + "2" + ], + "PropertyCompTwoPrim": null +} \ No newline at end of file diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/ODataBinderImpl.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/ODataBinderImpl.java index 1059c58fd..77764a539 100644 --- a/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/ODataBinderImpl.java +++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/serialization/ODataBinderImpl.java @@ -470,7 +470,7 @@ public class ODataBinderImpl implements ODataBinder { URIUtils.getURI(base, href), title, metadataETag)); } } - + for (ClientLink link : odataLinked.getNavigationLinks()) { if (!(link instanceof ClientInlineEntity) && !(link instanceof ClientInlineEntitySet)) { odataAnnotations(linked.getNavigationLink(link.getName()), link); @@ -858,10 +858,10 @@ public class ODataBinderImpl implements ODataBinder { } lcValue.add(getODataProperty(edmPropertyType, property)); } - + odataNavigationLinks(edmType, valuable.asComplex(), lcValue, metadataETag, contextURL); odataAnnotations(valuable.asComplex(), lcValue); - + value = lcValue; } else { if (valuable.isGeospatial()) { @@ -874,31 +874,40 @@ public class ODataBinderImpl implements ODataBinder { : EdmPrimitiveTypeKind.valueOfFQN(type.toString())). build(); } else if (valuable.isPrimitive() || valuable.getValueType() == null) { - // fixes non-string values treated as string when no type information is available at de-serialization level - if (type != null && !EdmPrimitiveTypeKind.String.getFullQualifiedName().equals(type) - && EdmPrimitiveType.EDM_NAMESPACE.equals(type.getNamespace()) - && valuable.asPrimitive() instanceof String) { - - final EdmPrimitiveType primitiveType = - EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.valueOf(type.getName())); - final Class<?> returnType = primitiveType.getDefaultType().isAssignableFrom(Calendar.class) - ? Timestamp.class : primitiveType.getDefaultType(); - try { - valuable.setValue(valuable.getValueType(), - primitiveType.valueOfString(valuable.asPrimitive().toString(), - null, null, Constants.DEFAULT_PRECISION, Constants.DEFAULT_SCALE, null, - returnType)); - } catch (EdmPrimitiveTypeException e) { - throw new IllegalArgumentException(e); - } + // fixes non-string values treated as string when no type information is available at de-serialization level + Edm edm = null; + if (client instanceof EdmEnabledODataClient && type != null) { + edm = ((EdmEnabledODataClient) client).getEdm(metadataETag); } - - value = client.getObjectFactory().newPrimitiveValueBuilder(). - setValue(valuable.asPrimitive()). - setType(type == null || !EdmPrimitiveType.EDM_NAMESPACE.equals(type.getNamespace()) - ? null - : EdmPrimitiveTypeKind.valueOfFQN(type.toString())). - build(); + if (edm != null && edm.getComplexType(type) != null) { + ClientComplexValue cValue = client.getObjectFactory().newComplexValue(type.toString()); + cValue.add(new ClientPropertyImpl(((Property)valuable).getName(), null)); + value = cValue; + } else { + if (type != null && !EdmPrimitiveTypeKind.String.getFullQualifiedName().equals(type) + && EdmPrimitiveType.EDM_NAMESPACE.equals(type.getNamespace()) + && valuable.asPrimitive() instanceof String) { + + final EdmPrimitiveType primitiveType = + EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.valueOf(type.getName())); + final Class<?> returnType = primitiveType.getDefaultType().isAssignableFrom(Calendar.class) + ? Timestamp.class : primitiveType.getDefaultType(); + try { + valuable.setValue(valuable.getValueType(), + primitiveType.valueOfString(valuable.asPrimitive().toString(), + null, null, Constants.DEFAULT_PRECISION, Constants.DEFAULT_SCALE, null, + returnType)); + } catch (EdmPrimitiveTypeException e) { + throw new IllegalArgumentException(e); + } + } + value = client.getObjectFactory().newPrimitiveValueBuilder(). + setValue(valuable.asPrimitive()). + setType(type == null || !EdmPrimitiveType.EDM_NAMESPACE.equals(type.getNamespace()) + ? null + : EdmPrimitiveTypeKind.valueOfFQN(type.toString())). + build(); + } } else if (valuable.isComplex()) { final ClientComplexValue cValue = client.getObjectFactory().newComplexValue(type == null ? null : type.toString());