[OLINGO-230] Implementation completed
This commit is contained in:
parent
6ae704d62f
commit
16d3b02886
|
@ -43,4 +43,6 @@ public interface ODataBinder extends CommonODataBinder {
|
|||
|
||||
@Override
|
||||
ODataProperty getODataProperty(Property property);
|
||||
|
||||
ODataProperty getODataProperty(Property property, URI base);
|
||||
}
|
||||
|
|
|
@ -47,7 +47,6 @@ import org.apache.olingo.client.api.http.HttpClientException;
|
|||
import org.apache.olingo.client.api.http.HttpMethod;
|
||||
import org.apache.olingo.client.core.communication.header.ODataHeadersImpl;
|
||||
import org.apache.olingo.commons.api.domain.ODataError;
|
||||
import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
|
||||
import org.apache.olingo.commons.api.format.ODataMediaFormat;
|
||||
import org.apache.olingo.commons.api.format.ODataPubFormat;
|
||||
import org.apache.olingo.commons.api.format.ODataValueFormat;
|
||||
|
@ -389,8 +388,9 @@ public class ODataRequestImpl<T extends Format> implements ODataRequest {
|
|||
}
|
||||
|
||||
// Add header for KeyAsSegment management
|
||||
if (odataClient.getServiceVersion() == ODataServiceVersion.V30
|
||||
if (odataClient.getConfiguration() instanceof Configuration
|
||||
&& ((Configuration) odataClient.getConfiguration()).isKeyAsSegment()) {
|
||||
|
||||
addCustomHeader(
|
||||
HeaderName.dataServiceUrlConventions.toString(),
|
||||
new ODataPreferences(odataClient.getServiceVersion()).keyAsSegment());
|
||||
|
|
|
@ -49,7 +49,7 @@ public abstract class AbstractEdmServiceMetadataImpl implements EdmServiceMetada
|
|||
public static EdmServiceMetadata getInstance(final ODataServiceVersion version,
|
||||
final List<? extends Schema> xmlSchemas) {
|
||||
|
||||
return version == ODataServiceVersion.V30
|
||||
return version.compareTo(ODataServiceVersion.V40) < 0
|
||||
? new org.apache.olingo.client.core.edm.v3.EdmServiceMetadataImpl(xmlSchemas)
|
||||
: new org.apache.olingo.client.core.edm.v4.EdmServiceMetadataImpl(xmlSchemas);
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ package org.apache.olingo.client.core.op;
|
|||
import java.io.StringWriter;
|
||||
import java.net.URI;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.olingo.client.api.CommonODataClient;
|
||||
import org.apache.olingo.client.api.data.ServiceDocument;
|
||||
|
@ -31,6 +32,7 @@ import org.apache.olingo.commons.api.Constants;
|
|||
import org.apache.olingo.commons.api.data.Entry;
|
||||
import org.apache.olingo.commons.api.data.Feed;
|
||||
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.Value;
|
||||
import org.apache.olingo.commons.api.domain.ODataCollectionValue;
|
||||
|
@ -43,6 +45,7 @@ import org.apache.olingo.commons.api.domain.ODataLink;
|
|||
import org.apache.olingo.commons.api.domain.ODataOperation;
|
||||
import org.apache.olingo.commons.api.domain.CommonODataProperty;
|
||||
import org.apache.olingo.commons.api.domain.ODataLinkType;
|
||||
import org.apache.olingo.commons.api.domain.ODataLinked;
|
||||
import org.apache.olingo.commons.api.domain.ODataServiceDocument;
|
||||
import org.apache.olingo.commons.api.domain.ODataValue;
|
||||
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
|
||||
|
@ -112,6 +115,30 @@ public abstract class AbstractODataBinder implements CommonODataBinder {
|
|||
return getEntry(entity, reference, true);
|
||||
}
|
||||
|
||||
protected void links(final ODataLinked odataLinked, final Linked linked, final Class<? extends Entry> reference) {
|
||||
// -------------------------------------------------------------
|
||||
// Append navigation links (handling inline entry / feed as well)
|
||||
// -------------------------------------------------------------
|
||||
// handle navigation links
|
||||
for (ODataLink link : odataLinked.getNavigationLinks()) {
|
||||
// append link
|
||||
LOG.debug("Append navigation link\n{}", link);
|
||||
linked.getNavigationLinks().add(getLink(link,
|
||||
ResourceFactory.formatForEntryClass(reference) == ODataPubFormat.ATOM));
|
||||
}
|
||||
// -------------------------------------------------------------
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// Append association links
|
||||
// -------------------------------------------------------------
|
||||
for (ODataLink link : odataLinked.getAssociationLinks()) {
|
||||
LOG.debug("Append association link\n{}", link);
|
||||
linked.getAssociationLinks().add(getLink(link,
|
||||
ResourceFactory.formatForEntryClass(reference) == ODataPubFormat.ATOM));
|
||||
}
|
||||
// -------------------------------------------------------------
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry getEntry(final CommonODataEntity entity, final Class<? extends Entry> reference, final boolean setType) {
|
||||
final Entry entry = ResourceFactory.newEntry(reference);
|
||||
|
@ -139,17 +166,7 @@ public abstract class AbstractODataBinder implements CommonODataBinder {
|
|||
}
|
||||
// -------------------------------------------------------------
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// Append navigation links (handling inline entry / feed as well)
|
||||
// -------------------------------------------------------------
|
||||
// handle navigation links
|
||||
for (ODataLink link : entity.getNavigationLinks()) {
|
||||
// append link
|
||||
LOG.debug("Append navigation link\n{}", link);
|
||||
entry.getNavigationLinks().add(getLink(link,
|
||||
ResourceFactory.formatForEntryClass(reference) == ODataPubFormat.ATOM));
|
||||
}
|
||||
// -------------------------------------------------------------
|
||||
links(entity, entry, reference);
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// Append edit-media links
|
||||
|
@ -161,16 +178,6 @@ public abstract class AbstractODataBinder implements CommonODataBinder {
|
|||
}
|
||||
// -------------------------------------------------------------
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// Append association links
|
||||
// -------------------------------------------------------------
|
||||
for (ODataLink link : entity.getAssociationLinks()) {
|
||||
LOG.debug("Append association link\n{}", link);
|
||||
entry.getAssociationLinks().add(getLink(link,
|
||||
ResourceFactory.formatForEntryClass(reference) == ODataPubFormat.ATOM));
|
||||
}
|
||||
// -------------------------------------------------------------
|
||||
|
||||
if (entity.isMediaEntity()) {
|
||||
entry.setMediaContentSource(entity.getMediaContentSource());
|
||||
entry.setMediaContentType(entity.getMediaContentType());
|
||||
|
@ -278,6 +285,34 @@ public abstract class AbstractODataBinder implements CommonODataBinder {
|
|||
return getODataEntity(resource, null);
|
||||
}
|
||||
|
||||
protected void odataLinks(final Linked linked, final ODataLinked odataLinked, final URI base) {
|
||||
for (Link link : linked.getNavigationLinks()) {
|
||||
final Entry inlineEntry = link.getInlineEntry();
|
||||
final Feed inlineFeed = link.getInlineFeed();
|
||||
|
||||
if (inlineEntry == null && inlineFeed == null) {
|
||||
final ODataLinkType linkType = link.getType() == null
|
||||
? ODataLinkType.ENTITY_NAVIGATION
|
||||
: ODataLinkType.fromString(client.getServiceVersion(), link.getRel(), link.getType());
|
||||
odataLinked.addLink(linkType == ODataLinkType.ENTITY_NAVIGATION
|
||||
? client.getObjectFactory().newEntityNavigationLink(link.getTitle(), base, link.getHref())
|
||||
: client.getObjectFactory().newEntitySetNavigationLink(link.getTitle(), base, link.getHref()));
|
||||
} else if (inlineEntry != null) {
|
||||
odataLinked.addLink(client.getObjectFactory().newInlineEntity(
|
||||
link.getTitle(), base, link.getHref(),
|
||||
getODataEntity(inlineEntry,
|
||||
inlineEntry.getBaseURI() == null ? base : inlineEntry.getBaseURI())));
|
||||
} else {
|
||||
odataLinked.addLink(client.getObjectFactory().newInlineEntitySet(
|
||||
link.getTitle(), base, link.getHref(),
|
||||
getODataEntitySet(inlineFeed,
|
||||
inlineFeed.getBaseURI() == null ? base : inlineFeed.getBaseURI())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void copyProperties(List<Property> src, CommonODataEntity dst, final URI base);
|
||||
|
||||
@Override
|
||||
public CommonODataEntity getODataEntity(final Entry resource, final URI defaultBaseURI) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
|
@ -292,7 +327,7 @@ public abstract class AbstractODataBinder implements CommonODataBinder {
|
|||
final CommonODataEntity entity = resource.getSelfLink() == null
|
||||
? client.getObjectFactory().newEntity(resource.getType())
|
||||
: client.getObjectFactory().newEntity(resource.getType(),
|
||||
URIUtils.getURI(base, resource.getSelfLink().getHref()));
|
||||
URIUtils.getURI(base, resource.getSelfLink().getHref()));
|
||||
|
||||
if (StringUtils.isNotBlank(resource.getETag())) {
|
||||
entity.setETag(resource.getETag());
|
||||
|
@ -306,29 +341,7 @@ public abstract class AbstractODataBinder implements CommonODataBinder {
|
|||
entity.addLink(client.getObjectFactory().newAssociationLink(link.getTitle(), base, link.getHref()));
|
||||
}
|
||||
|
||||
for (Link link : resource.getNavigationLinks()) {
|
||||
final Entry inlineEntry = link.getInlineEntry();
|
||||
final Feed inlineFeed = link.getInlineFeed();
|
||||
|
||||
if (inlineEntry == null && inlineFeed == null) {
|
||||
final ODataLinkType linkType = link.getType() == null
|
||||
? ODataLinkType.ENTITY_NAVIGATION
|
||||
: ODataLinkType.fromString(client.getServiceVersion(), link.getRel(), link.getType());
|
||||
entity.addLink(linkType == ODataLinkType.ENTITY_NAVIGATION
|
||||
? client.getObjectFactory().newEntityNavigationLink(link.getTitle(), base, link.getHref())
|
||||
: client.getObjectFactory().newEntitySetNavigationLink(link.getTitle(), base, link.getHref()));
|
||||
} else if (inlineEntry != null) {
|
||||
entity.addLink(client.getObjectFactory().newInlineEntity(
|
||||
link.getTitle(), base, link.getHref(),
|
||||
getODataEntity(inlineEntry,
|
||||
inlineEntry.getBaseURI() == null ? base : inlineEntry.getBaseURI())));
|
||||
} else {
|
||||
entity.addLink(client.getObjectFactory().newInlineEntitySet(
|
||||
link.getTitle(), base, link.getHref(),
|
||||
getODataEntitySet(inlineFeed,
|
||||
inlineFeed.getBaseURI() == null ? base : inlineFeed.getBaseURI())));
|
||||
}
|
||||
}
|
||||
odataLinks(resource, entity, base);
|
||||
|
||||
for (Link link : resource.getMediaEditLinks()) {
|
||||
entity.addLink(client.getObjectFactory().newMediaEditLink(link.getTitle(), base, link.getHref()));
|
||||
|
@ -346,14 +359,12 @@ public abstract class AbstractODataBinder implements CommonODataBinder {
|
|||
entity.setMediaETag(resource.getMediaETag());
|
||||
}
|
||||
|
||||
for (Property property : resource.getProperties()) {
|
||||
add(entity, getODataProperty(property));
|
||||
}
|
||||
copyProperties(resource.getProperties(), entity, base);
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
protected ODataValue getODataValue(final Property resource) {
|
||||
protected ODataValue getODataValue(final Property resource, final URI base) {
|
||||
ODataValue value = null;
|
||||
|
||||
final EdmTypeInfo typeInfo = resource.getType() == null
|
||||
|
@ -363,18 +374,18 @@ public abstract class AbstractODataBinder implements CommonODataBinder {
|
|||
value = client.getObjectFactory().newPrimitiveValueBuilder().
|
||||
setText(resource.getValue().asPrimitive().get()).
|
||||
setType(typeInfo == null
|
||||
? null
|
||||
: EdmPrimitiveTypeKind.valueOfFQN(
|
||||
client.getServiceVersion(), typeInfo.getFullQualifiedName().toString())).build();
|
||||
? null
|
||||
: EdmPrimitiveTypeKind.valueOfFQN(
|
||||
client.getServiceVersion(), typeInfo.getFullQualifiedName().toString())).build();
|
||||
} else if (resource.getValue().isGeospatial()) {
|
||||
value = client.getObjectFactory().newPrimitiveValueBuilder().
|
||||
setValue(resource.getValue().asGeospatial().get()).
|
||||
setType(typeInfo == null
|
||||
|| EdmPrimitiveTypeKind.Geography.getFullQualifiedName().equals(typeInfo.getFullQualifiedName())
|
||||
|| EdmPrimitiveTypeKind.Geometry.getFullQualifiedName().equals(typeInfo.getFullQualifiedName())
|
||||
? resource.getValue().asGeospatial().get().getEdmPrimitiveTypeKind()
|
||||
: EdmPrimitiveTypeKind.valueOfFQN(
|
||||
client.getServiceVersion(), typeInfo.getFullQualifiedName().toString())).build();
|
||||
|| EdmPrimitiveTypeKind.Geography.getFullQualifiedName().equals(typeInfo.getFullQualifiedName())
|
||||
|| EdmPrimitiveTypeKind.Geometry.getFullQualifiedName().equals(typeInfo.getFullQualifiedName())
|
||||
? resource.getValue().asGeospatial().get().getEdmPrimitiveTypeKind()
|
||||
: EdmPrimitiveTypeKind.valueOfFQN(
|
||||
client.getServiceVersion(), typeInfo.getFullQualifiedName().toString())).build();
|
||||
} else if (resource.getValue().isComplex()) {
|
||||
value = client.getObjectFactory().newComplexValue(typeInfo == null
|
||||
? null : typeInfo.getFullQualifiedName().toString());
|
||||
|
@ -389,7 +400,7 @@ public abstract class AbstractODataBinder implements CommonODataBinder {
|
|||
for (Value _value : resource.getValue().asCollection().get()) {
|
||||
final JSONPropertyImpl fake = new JSONPropertyImpl();
|
||||
fake.setValue(_value);
|
||||
value.asCollection().add(getODataValue(fake));
|
||||
value.asCollection().add(getODataValue(fake, base));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
package org.apache.olingo.client.core.op.impl.v3;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import org.apache.olingo.commons.api.data.v3.LinkCollection;
|
||||
import org.apache.olingo.client.api.domain.v3.ODataLinkCollection;
|
||||
import org.apache.olingo.client.api.op.v3.ODataBinder;
|
||||
|
@ -85,6 +86,13 @@ public class ODataBinderImpl extends AbstractODataBinder implements ODataBinder
|
|||
return (ODataEntitySet) super.getODataEntitySet(resource, defaultBaseURI);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void copyProperties(final List<Property> src, final CommonODataEntity dst, final URI base) {
|
||||
for (Property property : src) {
|
||||
add(dst, getODataProperty(property));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ODataEntity getODataEntity(final Entry resource) {
|
||||
return (ODataEntity) super.getODataEntity(resource);
|
||||
|
@ -97,7 +105,7 @@ public class ODataBinderImpl extends AbstractODataBinder implements ODataBinder
|
|||
|
||||
@Override
|
||||
public ODataProperty getODataProperty(final Property property) {
|
||||
return new ODataPropertyImpl(property.getName(), getODataValue(property));
|
||||
return new ODataPropertyImpl(property.getName(), getODataValue(property, null));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
package org.apache.olingo.client.core.op.impl.v4;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import org.apache.olingo.client.api.data.ServiceDocument;
|
||||
import org.apache.olingo.client.api.data.ServiceDocumentItem;
|
||||
import org.apache.olingo.client.api.op.v4.ODataBinder;
|
||||
|
@ -27,6 +28,7 @@ import org.apache.olingo.client.core.op.AbstractODataBinder;
|
|||
import org.apache.olingo.client.core.uri.URIUtils;
|
||||
import org.apache.olingo.commons.api.data.Entry;
|
||||
import org.apache.olingo.commons.api.data.Feed;
|
||||
import org.apache.olingo.commons.api.data.LinkedComplexValue;
|
||||
import org.apache.olingo.commons.api.data.Property;
|
||||
import org.apache.olingo.commons.api.data.Value;
|
||||
import org.apache.olingo.commons.api.domain.CommonODataEntity;
|
||||
|
@ -36,8 +38,10 @@ import org.apache.olingo.commons.api.domain.ODataServiceDocument;
|
|||
import org.apache.olingo.commons.api.domain.ODataValue;
|
||||
import org.apache.olingo.commons.api.domain.v4.ODataEntity;
|
||||
import org.apache.olingo.commons.api.domain.v4.ODataEntitySet;
|
||||
import org.apache.olingo.commons.api.domain.v4.ODataLinkedComplexValue;
|
||||
import org.apache.olingo.commons.api.domain.v4.ODataProperty;
|
||||
import org.apache.olingo.commons.core.data.EnumValueImpl;
|
||||
import org.apache.olingo.commons.core.data.LinkedComplexValueImpl;
|
||||
import org.apache.olingo.commons.core.domain.v4.ODataPropertyImpl;
|
||||
import org.apache.olingo.commons.core.edm.EdmTypeInfo;
|
||||
import org.apache.olingo.commons.core.op.ResourceFactory;
|
||||
|
@ -125,6 +129,19 @@ public class ODataBinderImpl extends AbstractODataBinder implements ODataBinder
|
|||
((org.apache.olingo.commons.api.domain.v4.ODataValue) value).asEnum().getValue());
|
||||
} else {
|
||||
valueResource = super.getValue(value, reference, setType);
|
||||
|
||||
if (value instanceof org.apache.olingo.commons.api.domain.v4.ODataValue
|
||||
&& ((org.apache.olingo.commons.api.domain.v4.ODataValue) value).isLinkedComplex()) {
|
||||
|
||||
final LinkedComplexValue lcValueResource = new LinkedComplexValueImpl();
|
||||
lcValueResource.get().addAll(valueResource.asComplex().get());
|
||||
|
||||
final ODataLinkedComplexValue linked =
|
||||
((org.apache.olingo.commons.api.domain.v4.ODataValue) value).asLinkedComplex();
|
||||
links(linked, lcValueResource, reference);
|
||||
|
||||
valueResource = lcValueResource;
|
||||
}
|
||||
}
|
||||
return valueResource;
|
||||
}
|
||||
|
@ -139,6 +156,13 @@ public class ODataBinderImpl extends AbstractODataBinder implements ODataBinder
|
|||
return (ODataEntitySet) super.getODataEntitySet(resource, defaultBaseURI);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void copyProperties(final List<Property> src, final CommonODataEntity dst, final URI base) {
|
||||
for (Property property : src) {
|
||||
add(dst, getODataProperty(property, base));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ODataEntity getODataEntity(final Entry resource) {
|
||||
return (ODataEntity) super.getODataEntity(resource);
|
||||
|
@ -153,21 +177,38 @@ public class ODataBinderImpl extends AbstractODataBinder implements ODataBinder
|
|||
|
||||
@Override
|
||||
public ODataProperty getODataProperty(final Property property) {
|
||||
return new ODataPropertyImpl(property.getName(), getODataValue(property));
|
||||
return getODataProperty(property, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ODataValue getODataValue(final Property resource) {
|
||||
public ODataProperty getODataProperty(final Property property, final URI base) {
|
||||
return new ODataPropertyImpl(property.getName(), getODataValue(property, base));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ODataValue getODataValue(final Property resource, final URI base) {
|
||||
final EdmTypeInfo typeInfo = resource.getType() == null
|
||||
? null
|
||||
: new EdmTypeInfo.Builder().setTypeExpression(resource.getType()).build();
|
||||
|
||||
ODataValue value;
|
||||
if (resource.getValue().isEnum()) {
|
||||
final EdmTypeInfo typeInfo = resource.getType() == null
|
||||
? null
|
||||
: new EdmTypeInfo.Builder().setTypeExpression(resource.getType()).build();
|
||||
value = ((ODataClient) client).getObjectFactory().newEnumValue(
|
||||
typeInfo == null ? null : typeInfo.getFullQualifiedName().toString(),
|
||||
resource.getValue().asEnum().get());
|
||||
} else if (resource.getValue().isLinkedComplex()) {
|
||||
final ODataLinkedComplexValue lcValue = ((ODataClient) client).getObjectFactory().
|
||||
newLinkedComplexValue(typeInfo == null ? null : typeInfo.getFullQualifiedName().toString());
|
||||
|
||||
for (Property property : resource.getValue().asComplex().get()) {
|
||||
lcValue.add(getODataProperty(property));
|
||||
}
|
||||
|
||||
odataLinks(resource.getValue().asLinkedComplex(), lcValue, base);
|
||||
|
||||
value = lcValue;
|
||||
} else {
|
||||
value = super.getODataValue(resource);
|
||||
value = super.getODataValue(resource, base);
|
||||
}
|
||||
|
||||
return value;
|
||||
|
|
|
@ -161,7 +161,7 @@ public final class URIUtils {
|
|||
|
||||
private static String prefix(final ODataServiceVersion version, final EdmPrimitiveTypeKind typeKind) {
|
||||
String result = StringUtils.EMPTY;
|
||||
if (version == ODataServiceVersion.V30) {
|
||||
if (version.compareTo(ODataServiceVersion.V40) < 0) {
|
||||
switch (typeKind) {
|
||||
case Guid:
|
||||
result = "guid'";
|
||||
|
@ -188,7 +188,7 @@ public final class URIUtils {
|
|||
|
||||
private static String suffix(final ODataServiceVersion version, final EdmPrimitiveTypeKind typeKind) {
|
||||
String result = StringUtils.EMPTY;
|
||||
if (version == ODataServiceVersion.V30) {
|
||||
if (version.compareTo(ODataServiceVersion.V40) < 0) {
|
||||
switch (typeKind) {
|
||||
case Guid:
|
||||
case DateTime:
|
||||
|
@ -222,7 +222,7 @@ public final class URIUtils {
|
|||
private static String timestamp(final ODataServiceVersion version, final Timestamp timestamp)
|
||||
throws UnsupportedEncodingException, EdmPrimitiveTypeException {
|
||||
|
||||
return version == ODataServiceVersion.V30
|
||||
return version.compareTo(ODataServiceVersion.V40) < 0
|
||||
? prefix(version, EdmPrimitiveTypeKind.DateTime)
|
||||
+ URLEncoder.encode(EdmDateTime.getInstance().
|
||||
valueToString(timestamp, null, null, Constants.DEFAULT_PRECISION, Constants.DEFAULT_SCALE, null),
|
||||
|
@ -238,7 +238,7 @@ public final class URIUtils {
|
|||
|
||||
String result;
|
||||
if (calendar.get(Calendar.ZONE_OFFSET) == 0) {
|
||||
if (version == ODataServiceVersion.V30) {
|
||||
if (version.compareTo(ODataServiceVersion.V40) < 0) {
|
||||
result = prefix(version, EdmPrimitiveTypeKind.DateTime)
|
||||
+ URLEncoder.encode(EdmDateTime.getInstance().
|
||||
valueToString(calendar, null, null, Constants.DEFAULT_PRECISION, Constants.DEFAULT_SCALE, null),
|
||||
|
@ -271,7 +271,7 @@ public final class URIUtils {
|
|||
private static String duration(final ODataServiceVersion version, final Duration duration)
|
||||
throws UnsupportedEncodingException, EdmPrimitiveTypeException {
|
||||
|
||||
return version == ODataServiceVersion.V30
|
||||
return version.compareTo(ODataServiceVersion.V40) < 0
|
||||
? EdmTime.getInstance().toUriLiteral(URLEncoder.encode(EdmTime.getInstance().
|
||||
valueToString(duration, null, null,
|
||||
Constants.DEFAULT_PRECISION, Constants.DEFAULT_SCALE, null), Constants.UTF8))
|
||||
|
@ -308,7 +308,7 @@ public final class URIUtils {
|
|||
try {
|
||||
if (obj == null) {
|
||||
value = Constants.ATTR_NULL;
|
||||
} else if (version == ODataServiceVersion.V40 && obj instanceof Collection) {
|
||||
} else if (version.compareTo(ODataServiceVersion.V40) >= 0 && obj instanceof Collection) {
|
||||
final StringBuffer buffer = new StringBuffer("[");
|
||||
for (@SuppressWarnings("unchecked")
|
||||
final Iterator<Object> itor = ((Collection<Object>) obj).iterator(); itor.hasNext();) {
|
||||
|
@ -320,7 +320,7 @@ public final class URIUtils {
|
|||
buffer.append(']');
|
||||
|
||||
value = buffer.toString();
|
||||
} else if (version == ODataServiceVersion.V40 && obj instanceof Map) {
|
||||
} else if (version.compareTo(ODataServiceVersion.V40) >= 0 && obj instanceof Map) {
|
||||
final StringBuffer buffer = new StringBuffer("{");
|
||||
for (@SuppressWarnings("unchecked")
|
||||
final Iterator<Map.Entry<Object, Object>> itor =
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.apache.olingo.commons.api.domain.ODataInlineEntitySet;
|
|||
import org.apache.olingo.commons.api.domain.ODataLink;
|
||||
import org.apache.olingo.commons.api.domain.ODataLinkType;
|
||||
import org.apache.olingo.commons.api.domain.v4.ODataEntity;
|
||||
import org.apache.olingo.commons.api.domain.v4.ODataLinkedComplexValue;
|
||||
import org.apache.olingo.commons.api.domain.v4.ODataProperty;
|
||||
import org.apache.olingo.commons.api.domain.v4.ODataValue;
|
||||
import org.apache.olingo.commons.api.format.ODataPubFormat;
|
||||
|
@ -247,4 +248,31 @@ public class EntityTest extends AbstractTest {
|
|||
public void jsonRef() {
|
||||
ref(ODataPubFormat.JSON);
|
||||
}
|
||||
|
||||
private void complexNavigationProperties(final ODataPubFormat format) {
|
||||
final InputStream input = getClass().getResourceAsStream("entity.withcomplexnavigation." + getSuffix(format));
|
||||
final ODataEntity entity = getClient().getBinder().getODataEntity(
|
||||
getClient().getDeserializer().toEntry(input, format).getObject());
|
||||
assertNotNull(entity);
|
||||
|
||||
final ODataLinkedComplexValue addressValue = entity.getProperty("Address").getLinkedComplexValue();
|
||||
assertNotNull(addressValue);
|
||||
assertNotNull(addressValue.getNavigationLink("Country"));
|
||||
|
||||
// ETag is not serialized
|
||||
entity.setETag(null);
|
||||
final ODataEntity written = getClient().getBinder().getODataEntity(getClient().getBinder().
|
||||
getEntry(entity, ResourceFactory.entryClassForFormat(format == ODataPubFormat.ATOM)));
|
||||
assertEquals(entity, written);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void atomComplexNavigationProperties() {
|
||||
complexNavigationProperties(ODataPubFormat.ATOM);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void jsonComplexNavigationProperties() {
|
||||
complexNavigationProperties(ODataPubFormat.JSON);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,6 +58,9 @@ public class JSONTest extends AbstractTest {
|
|||
if (node.has(Constants.JSON_CONTEXT)) {
|
||||
node.remove(Constants.JSON_CONTEXT);
|
||||
}
|
||||
if (node.has(getClient().getServiceVersion().getJSONMap().get(ODataServiceVersion.JSON_ETAG))) {
|
||||
node.remove(getClient().getServiceVersion().getJSONMap().get(ODataServiceVersion.JSON_ETAG));
|
||||
}
|
||||
if (node.has(getClient().getServiceVersion().getJSONMap().get(ODataServiceVersion.JSON_TYPE))) {
|
||||
node.remove(getClient().getServiceVersion().getJSONMap().get(ODataServiceVersion.JSON_TYPE));
|
||||
}
|
||||
|
@ -140,7 +143,6 @@ public class JSONTest extends AbstractTest {
|
|||
@Test
|
||||
public void additionalEntries() throws Exception {
|
||||
entry("entity.minimal", getODataPubFormat());
|
||||
// entry("entity.full", getODataPubFormat());
|
||||
entry("entity.primitive", getODataPubFormat());
|
||||
entry("entity.complex", getODataPubFormat());
|
||||
entry("entity.collection.primitive", getODataPubFormat());
|
||||
|
@ -153,6 +155,7 @@ public class JSONTest extends AbstractTest {
|
|||
entry("VipCustomer", getODataPubFormat());
|
||||
entry("Advertisements_f89dee73-af9f-4cd4-b330-db93c25ff3c7", getODataPubFormat());
|
||||
entry("entityReference", getODataPubFormat());
|
||||
entry("entity.withcomplexnavigation", getODataPubFormat());
|
||||
}
|
||||
|
||||
protected void property(final String filename, final ODataFormat format) throws Exception {
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
|
||||
<xsl:template match="atom:updated"/>
|
||||
<xsl:template match="atom:author"/>
|
||||
<xsl:template match="atom:summary"/>
|
||||
<xsl:template match="atom:title">
|
||||
<xsl:if test="string-length(.) > 0">
|
||||
<title type="{@type}">
|
||||
|
@ -38,7 +39,7 @@
|
|||
|
||||
<xsl:template match="m:action"/>
|
||||
|
||||
<xsl:template match="@*[name() = 'm:etag' or name() = 'm:context']"/>
|
||||
<xsl:template match="@*[name() = 'm:etag' or name() = 'm:context' or name() = 'm:metadata-etag']"/>
|
||||
|
||||
<xsl:template match="node()|@*">
|
||||
<xsl:copy>
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<entry xmlns="http://www.w3.org/2005/Atom"
|
||||
xmlns:m="http://docs.oasis-open.org/odata/ns/metadata"
|
||||
xmlns:d="http://docs.oasis-open.org/odata/ns/data"
|
||||
xml:base="http://host/service/"
|
||||
m:context="$metadata#Customers/$entity"
|
||||
m:metadata-etag="W/"MjAxMy0wNS0xM1QxNDo1NFo="">
|
||||
<id>http://host/service/$metadata#Customers('ALFKI')</id>
|
||||
<title/>
|
||||
<summary/>
|
||||
<updated>2012-03-30T07:11:05Z</updated>
|
||||
<author>
|
||||
<name/>
|
||||
</author>
|
||||
<link rel="edit" title="Customer" href="Customers('ALFKI')"/>
|
||||
<link rel="http://docs.oasis-open.org/odata/ns/related/Orders"
|
||||
type="application/atom+xml;type=feed"
|
||||
title="Orders" href="Customers('ALFKI')/Orders"/>
|
||||
<link rel="http://docs.oasis-open.org/odata/ns/related/Supplier"
|
||||
type="application/atom+xml;type=entry"
|
||||
title="Supplier" href="Customers('ALFKI')/Supplier"/>
|
||||
<category term="#ODataDemo.Customer"
|
||||
scheme="http://docs.oasis-open.org/odata/ns/scheme"/>
|
||||
<content type="application/xml">
|
||||
<m:properties>
|
||||
<d:ID>ALFKI</d:ID>
|
||||
<d:CompanyName>Alfreds Futterkiste</d:CompanyName>
|
||||
<d:ContactName>Maria Anders</d:ContactName>
|
||||
<d:ContactTitle>Sales Representative</d:ContactTitle>
|
||||
<d:Phone>030-0074321</d:Phone>
|
||||
<d:Fax>030-0076545</d:Fax>
|
||||
<d:Address>
|
||||
<d:Street>Obere Str. 57</d:Street>
|
||||
<d:City>Berlin</d:City>
|
||||
<d:Region m:null="true"/>
|
||||
<d:PostalCode>D-12209</d:PostalCode>
|
||||
<link rel="http://docs.oasis-open.org/odata/ns/related/Country"
|
||||
type="application/atom+xml;type=entry"
|
||||
title="Country"
|
||||
href="Customers('ALFKI')/Address/Country"/>
|
||||
</d:Address>
|
||||
</m:properties>
|
||||
</content>
|
||||
</entry>
|
|
@ -22,7 +22,7 @@ import org.apache.olingo.commons.api.domain.ODataOperation;
|
|||
import java.net.URI;
|
||||
import java.util.List;
|
||||
|
||||
public interface Entry {
|
||||
public interface Entry extends Linked {
|
||||
|
||||
/**
|
||||
* Gets ETag.
|
||||
|
@ -94,20 +94,6 @@ public interface Entry {
|
|||
*/
|
||||
void setEditLink(Link editLink);
|
||||
|
||||
/**
|
||||
* Gets association links.
|
||||
*
|
||||
* @return association links.
|
||||
*/
|
||||
List<Link> getAssociationLinks();
|
||||
|
||||
/**
|
||||
* Gets navigation links.
|
||||
*
|
||||
* @return links.
|
||||
*/
|
||||
List<Link> getNavigationLinks();
|
||||
|
||||
/**
|
||||
* Gets media entity links.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* 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.commons.api.data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface Linked {
|
||||
|
||||
/**
|
||||
* Gets association links.
|
||||
*
|
||||
* @return association links.
|
||||
*/
|
||||
List<Link> getAssociationLinks();
|
||||
|
||||
/**
|
||||
* Gets navigation links.
|
||||
*
|
||||
* @return links.
|
||||
*/
|
||||
List<Link> getNavigationLinks();
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* 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.commons.api.data;
|
||||
|
||||
public interface LinkedComplexValue extends ComplexValue, Linked {
|
||||
|
||||
}
|
|
@ -30,6 +30,8 @@ public interface Value {
|
|||
|
||||
boolean isComplex();
|
||||
|
||||
boolean isLinkedComplex();
|
||||
|
||||
boolean isCollection();
|
||||
|
||||
Object get();
|
||||
|
@ -44,5 +46,7 @@ public interface Value {
|
|||
|
||||
ComplexValue asComplex();
|
||||
|
||||
LinkedComplexValue asLinkedComplex();
|
||||
|
||||
CollectionValue asCollection();
|
||||
}
|
||||
|
|
|
@ -24,12 +24,26 @@ import java.util.List;
|
|||
/**
|
||||
* OData entity.
|
||||
*/
|
||||
public interface CommonODataEntity extends ODataInvokeResult {
|
||||
public interface CommonODataEntity extends ODataLinked, ODataInvokeResult {
|
||||
|
||||
String getName();
|
||||
|
||||
URI getLink();
|
||||
|
||||
/**
|
||||
* Returns OData entity edit link.
|
||||
*
|
||||
* @return entity edit link.
|
||||
*/
|
||||
URI getEditLink();
|
||||
|
||||
/**
|
||||
* Sets OData entity edit link.
|
||||
*
|
||||
* @param editLink edit link.
|
||||
*/
|
||||
void setEditLink(URI editLink);
|
||||
|
||||
/**
|
||||
* Gets ETag.
|
||||
*
|
||||
|
@ -74,52 +88,6 @@ public interface CommonODataEntity extends ODataInvokeResult {
|
|||
*/
|
||||
List<? extends CommonODataProperty> getProperties();
|
||||
|
||||
/**
|
||||
* Puts the given link into one of available lists, based on its type.
|
||||
*
|
||||
* @param link to be added
|
||||
* @return <tt>true</tt> if the given link was added in one of available lists
|
||||
*/
|
||||
boolean addLink(ODataLink link);
|
||||
|
||||
/**
|
||||
* Removes the given link from any list (association, navigation, edit-media).
|
||||
*
|
||||
* @param link to be removed
|
||||
* @return <tt>true</tt> if the given link was contained in one of available lists
|
||||
*/
|
||||
boolean removeLink(ODataLink link);
|
||||
|
||||
/**
|
||||
* Gets association link with given name, if available, otherwise <tt>null</tt>.
|
||||
*
|
||||
* @param name candidate link name
|
||||
* @return association link with given name, if available, otherwise <tt>null</tt>
|
||||
*/
|
||||
ODataLink getAssociationLink(String name);
|
||||
|
||||
/**
|
||||
* Returns all entity association links.
|
||||
*
|
||||
* @return OData entity links.
|
||||
*/
|
||||
List<ODataLink> getAssociationLinks();
|
||||
|
||||
/**
|
||||
* Gets navigation link with given name, if available, otherwise <tt>null</tt>.
|
||||
*
|
||||
* @param name candidate link name
|
||||
* @return navigation link with given name, if available, otherwise <tt>null</tt>
|
||||
*/
|
||||
ODataLink getNavigationLink(String name);
|
||||
|
||||
/**
|
||||
* Returns all entity navigation links (including inline entities / feeds).
|
||||
*
|
||||
* @return OData entity links.
|
||||
*/
|
||||
List<ODataLink> getNavigationLinks();
|
||||
|
||||
/**
|
||||
* Gets media-edit link with given name, if available, otherwise <tt>null</tt>.
|
||||
*
|
||||
|
@ -135,20 +103,6 @@ public interface CommonODataEntity extends ODataInvokeResult {
|
|||
*/
|
||||
List<ODataLink> getEditMediaLinks();
|
||||
|
||||
/**
|
||||
* Returns OData entity edit link.
|
||||
*
|
||||
* @return entity edit link.
|
||||
*/
|
||||
URI getEditLink();
|
||||
|
||||
/**
|
||||
* Sets OData entity edit link.
|
||||
*
|
||||
* @param editLink edit link.
|
||||
*/
|
||||
void setEditLink(URI editLink);
|
||||
|
||||
/**
|
||||
* TRUE if read-only entity.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* 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.commons.api.domain;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface ODataLinked {
|
||||
|
||||
/**
|
||||
* Puts the given link into one of available lists, based on its type.
|
||||
*
|
||||
* @param link to be added
|
||||
* @return <tt>true</tt> if the given link was added in one of available lists
|
||||
*/
|
||||
boolean addLink(ODataLink link);
|
||||
|
||||
/**
|
||||
* Removes the given link from any list (association, navigation, edit-media).
|
||||
*
|
||||
* @param link to be removed
|
||||
* @return <tt>true</tt> if the given link was contained in one of available lists
|
||||
*/
|
||||
boolean removeLink(ODataLink link);
|
||||
|
||||
/**
|
||||
* Gets association link with given name, if available, otherwise <tt>null</tt>.
|
||||
*
|
||||
* @param name candidate link name
|
||||
* @return association link with given name, if available, otherwise <tt>null</tt>
|
||||
*/
|
||||
ODataLink getAssociationLink(String name);
|
||||
|
||||
/**
|
||||
* Returns all entity association links.
|
||||
*
|
||||
* @return OData entity links.
|
||||
*/
|
||||
List<ODataLink> getAssociationLinks();
|
||||
|
||||
/**
|
||||
* Gets navigation link with given name, if available, otherwise <tt>null</tt>.
|
||||
*
|
||||
* @param name candidate link name
|
||||
* @return navigation link with given name, if available, otherwise <tt>null</tt>
|
||||
*/
|
||||
ODataLink getNavigationLink(String name);
|
||||
|
||||
/**
|
||||
* Returns all entity navigation links (including inline entities / feeds).
|
||||
*
|
||||
* @return OData entity links.
|
||||
*/
|
||||
List<ODataLink> getNavigationLinks();
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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.commons.api.domain.v4;
|
||||
|
||||
import org.apache.olingo.commons.api.domain.ODataComplexValue;
|
||||
import org.apache.olingo.commons.api.domain.ODataLinked;
|
||||
|
||||
public interface ODataLinkedComplexValue extends ODataValue, ODataLinked, ODataComplexValue<ODataProperty> {
|
||||
|
||||
}
|
|
@ -44,6 +44,8 @@ public interface ODataObjectFactory extends CommonODataObjectFactory {
|
|||
@Override
|
||||
ODataComplexValue<ODataProperty> newComplexValue(String typeName);
|
||||
|
||||
ODataLinkedComplexValue newLinkedComplexValue(String typeName);
|
||||
|
||||
@Override
|
||||
ODataCollectionValue<ODataValue> newCollectionValue(String typeName);
|
||||
|
||||
|
|
|
@ -37,6 +37,13 @@ public interface ODataProperty extends CommonODataProperty {
|
|||
* @return complex value if exists; null otherwise.
|
||||
*/
|
||||
ODataComplexValue<ODataProperty> getComplexValue();
|
||||
|
||||
/**
|
||||
* Gets complex value with link information (if available).
|
||||
*
|
||||
* @return complex value if exists; null otherwise.
|
||||
*/
|
||||
ODataLinkedComplexValue getLinkedComplexValue();
|
||||
|
||||
/**
|
||||
* Checks if has enum value.
|
||||
|
|
|
@ -20,6 +20,20 @@ package org.apache.olingo.commons.api.domain.v4;
|
|||
|
||||
public interface ODataValue extends org.apache.olingo.commons.api.domain.ODataValue {
|
||||
|
||||
/**
|
||||
* Check is is a linked complex value.
|
||||
*
|
||||
* @return 'TRUE' if linked complex; 'FALSE' otherwise.
|
||||
*/
|
||||
boolean isLinkedComplex();
|
||||
|
||||
/**
|
||||
* Casts to complex value with link information (if available).
|
||||
*
|
||||
* @return complex value with link information.
|
||||
*/
|
||||
ODataLinkedComplexValue asLinkedComplex();
|
||||
|
||||
/**
|
||||
* Check is is an enum value.
|
||||
*
|
||||
|
|
|
@ -81,7 +81,7 @@ abstract class AbstractAtomDealer {
|
|||
new QName(version.getNamespaceMap().get(ODataServiceVersion.NS_METADATA), Constants.PROPERTIES);
|
||||
this.typeQName = new QName(version.getNamespaceMap().get(ODataServiceVersion.NS_METADATA), Constants.ATTR_TYPE);
|
||||
this.nullQName = new QName(version.getNamespaceMap().get(ODataServiceVersion.NS_METADATA), Constants.ATTR_NULL);
|
||||
this.elementQName = version == ODataServiceVersion.V30
|
||||
this.elementQName = version.compareTo(ODataServiceVersion.V40) < 0
|
||||
? new QName(version.getNamespaceMap().get(ODataServiceVersion.NS_DATASERVICES), Constants.ELEM_ELEMENT)
|
||||
: new QName(version.getNamespaceMap().get(ODataServiceVersion.NS_METADATA), Constants.ELEM_ELEMENT);
|
||||
this.countQName =
|
||||
|
|
|
@ -18,17 +18,26 @@
|
|||
*/
|
||||
package org.apache.olingo.commons.core.data;
|
||||
|
||||
import com.fasterxml.jackson.core.ObjectCodec;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.olingo.commons.api.data.CollectionValue;
|
||||
import org.apache.olingo.commons.api.data.ComplexValue;
|
||||
import org.apache.olingo.commons.api.data.Container;
|
||||
import org.apache.olingo.commons.api.data.Linked;
|
||||
import org.apache.olingo.commons.api.data.Value;
|
||||
import org.apache.olingo.commons.api.domain.ODataLinkType;
|
||||
import org.apache.olingo.commons.api.domain.ODataPropertyType;
|
||||
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
|
||||
import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
|
||||
import org.apache.olingo.commons.core.edm.EdmTypeInfo;
|
||||
|
||||
abstract class AbstractJsonDeserializer<T> extends ODataJacksonDeserializer<Container<T>> {
|
||||
|
@ -42,6 +51,76 @@ abstract class AbstractJsonDeserializer<T> extends ODataJacksonDeserializer<Cont
|
|||
return geoDeserializer;
|
||||
}
|
||||
|
||||
protected String getTitle(final Map.Entry<String, JsonNode> entry) {
|
||||
return entry.getKey().substring(0, entry.getKey().indexOf('@'));
|
||||
}
|
||||
|
||||
protected String setInline(final String name, final String suffix, final JsonNode tree,
|
||||
final ObjectCodec codec, final LinkImpl link) throws IOException {
|
||||
|
||||
final String entryNamePrefix = name.substring(0, name.indexOf(suffix));
|
||||
if (tree.has(entryNamePrefix)) {
|
||||
final JsonNode inline = tree.path(entryNamePrefix);
|
||||
|
||||
if (inline instanceof ObjectNode) {
|
||||
link.setType(ODataLinkType.ENTITY_NAVIGATION.toString());
|
||||
|
||||
link.setInlineEntry(inline.traverse(codec).<Container<JSONEntryImpl>>readValueAs(
|
||||
new TypeReference<JSONEntryImpl>() {
|
||||
}).getObject());
|
||||
}
|
||||
|
||||
if (inline instanceof ArrayNode) {
|
||||
link.setType(ODataLinkType.ENTITY_SET_NAVIGATION.toString());
|
||||
|
||||
final JSONFeedImpl feed = new JSONFeedImpl();
|
||||
final Iterator<JsonNode> entries = ((ArrayNode) inline).elements();
|
||||
while (entries.hasNext()) {
|
||||
feed.getEntries().add(entries.next().traverse(codec).<Container<JSONEntryImpl>>readValuesAs(
|
||||
new TypeReference<JSONEntryImpl>() {
|
||||
}).next().getObject());
|
||||
}
|
||||
|
||||
link.setInlineFeed(feed);
|
||||
}
|
||||
}
|
||||
return entryNamePrefix;
|
||||
}
|
||||
|
||||
protected void links(final Map.Entry<String, JsonNode> field, final Linked linked, final Set<String> toRemove,
|
||||
final JsonNode tree, final ObjectCodec codec) throws IOException {
|
||||
|
||||
if (field.getKey().endsWith(jsonNavigationLink)) {
|
||||
final LinkImpl link = new LinkImpl();
|
||||
link.setTitle(getTitle(field));
|
||||
link.setRel(version.getNamespaceMap().get(ODataServiceVersion.NAVIGATION_LINK_REL) + getTitle(field));
|
||||
|
||||
if (field.getValue().isValueNode()) {
|
||||
link.setHref(field.getValue().textValue());
|
||||
link.setType(ODataLinkType.ENTITY_NAVIGATION.toString());
|
||||
}
|
||||
// NOTE: this should be expected to happen, but it isn't - at least up to OData 4.0
|
||||
/* if (field.getValue().isArray()) {
|
||||
* link.setHref(field.getValue().asText());
|
||||
* link.setType(ODataLinkType.ENTITY_SET_NAVIGATION.toString());
|
||||
* } */
|
||||
|
||||
linked.getNavigationLinks().add(link);
|
||||
|
||||
toRemove.add(field.getKey());
|
||||
toRemove.add(setInline(field.getKey(), jsonNavigationLink, tree, codec, link));
|
||||
} else if (field.getKey().endsWith(jsonAssociationLink)) {
|
||||
final LinkImpl link = new LinkImpl();
|
||||
link.setTitle(getTitle(field));
|
||||
link.setRel(version.getNamespaceMap().get(ODataServiceVersion.ASSOCIATION_LINK_REL) + getTitle(field));
|
||||
link.setHref(field.getValue().textValue());
|
||||
link.setType(ODataLinkType.ASSOCIATION.toString());
|
||||
linked.getAssociationLinks().add(link);
|
||||
|
||||
toRemove.add(field.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
protected EdmPrimitiveTypeKind getPrimitiveType(final JsonNode node) {
|
||||
EdmPrimitiveTypeKind result = EdmPrimitiveTypeKind.String;
|
||||
|
||||
|
@ -88,8 +167,20 @@ abstract class AbstractJsonDeserializer<T> extends ODataJacksonDeserializer<Cont
|
|||
return value;
|
||||
}
|
||||
|
||||
private ComplexValue fromComplex(final JsonNode node) {
|
||||
final ComplexValue value = new ComplexValueImpl();
|
||||
private ComplexValue fromComplex(final ObjectNode node, final ObjectCodec codec) throws IOException {
|
||||
final ComplexValue value = version.compareTo(ODataServiceVersion.V40) < 0
|
||||
? new ComplexValueImpl()
|
||||
: new LinkedComplexValueImpl();
|
||||
|
||||
if (value.isLinkedComplex()) {
|
||||
final Set<String> toRemove = new HashSet<String>();
|
||||
for (final Iterator<Map.Entry<String, JsonNode>> itor = node.fields(); itor.hasNext();) {
|
||||
final Map.Entry<String, JsonNode> field = itor.next();
|
||||
|
||||
links(field, value.asLinkedComplex(), toRemove, node, codec);
|
||||
}
|
||||
node.remove(toRemove);
|
||||
}
|
||||
|
||||
String type = null;
|
||||
for (final Iterator<Map.Entry<String, JsonNode>> itor = node.fields(); itor.hasNext();) {
|
||||
|
@ -103,7 +194,7 @@ abstract class AbstractJsonDeserializer<T> extends ODataJacksonDeserializer<Cont
|
|||
property.setType(type);
|
||||
type = null;
|
||||
|
||||
value(property, field.getValue());
|
||||
value(property, field.getValue(), codec);
|
||||
value.get().add(property);
|
||||
}
|
||||
}
|
||||
|
@ -111,7 +202,9 @@ abstract class AbstractJsonDeserializer<T> extends ODataJacksonDeserializer<Cont
|
|||
return value;
|
||||
}
|
||||
|
||||
private CollectionValue fromCollection(final Iterator<JsonNode> nodeItor, final EdmTypeInfo typeInfo) {
|
||||
private CollectionValue fromCollection(final Iterator<JsonNode> nodeItor, final EdmTypeInfo typeInfo,
|
||||
final ObjectCodec codec) throws IOException {
|
||||
|
||||
final CollectionValueImpl value = new CollectionValueImpl();
|
||||
|
||||
final EdmTypeInfo type = typeInfo == null
|
||||
|
@ -131,14 +224,16 @@ abstract class AbstractJsonDeserializer<T> extends ODataJacksonDeserializer<Cont
|
|||
if (child.has(jsonType)) {
|
||||
((ObjectNode) child).remove(jsonType);
|
||||
}
|
||||
value.get().add(fromComplex(child));
|
||||
value.get().add(fromComplex((ObjectNode) child, codec));
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
protected void value(final JSONPropertyImpl property, final JsonNode node) {
|
||||
protected void value(final JSONPropertyImpl property, final JsonNode node, final ObjectCodec codec)
|
||||
throws IOException {
|
||||
|
||||
final EdmTypeInfo typeInfo = StringUtils.isBlank(property.getType())
|
||||
? null
|
||||
: new EdmTypeInfo.Builder().setTypeExpression(property.getType()).build();
|
||||
|
@ -155,7 +250,7 @@ abstract class AbstractJsonDeserializer<T> extends ODataJacksonDeserializer<Cont
|
|||
|
||||
switch (propType) {
|
||||
case COLLECTION:
|
||||
property.setValue(fromCollection(node.elements(), typeInfo));
|
||||
property.setValue(fromCollection(node.elements(), typeInfo, codec));
|
||||
break;
|
||||
|
||||
case COMPLEX:
|
||||
|
@ -163,7 +258,7 @@ abstract class AbstractJsonDeserializer<T> extends ODataJacksonDeserializer<Cont
|
|||
property.setType(node.get(jsonType).asText());
|
||||
((ObjectNode) node).remove(jsonType);
|
||||
}
|
||||
property.setValue(fromComplex(node));
|
||||
property.setValue(fromComplex((ObjectNode) node, codec));
|
||||
break;
|
||||
|
||||
case ENUM:
|
||||
|
|
|
@ -20,12 +20,22 @@ package org.apache.olingo.commons.core.data;
|
|||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.math.NumberUtils;
|
||||
import org.apache.olingo.commons.api.Constants;
|
||||
import org.apache.olingo.commons.api.data.CollectionValue;
|
||||
import org.apache.olingo.commons.api.data.Entry;
|
||||
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.Value;
|
||||
import org.apache.olingo.commons.api.domain.ODataLinkType;
|
||||
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
|
||||
import org.apache.olingo.commons.core.edm.EdmTypeInfo;
|
||||
|
||||
|
@ -39,6 +49,50 @@ abstract class AbstractJsonSerializer<T> extends ODataJacksonSerializer<T> {
|
|||
|
||||
private final JSONGeoValueSerializer geoSerializer = new JSONGeoValueSerializer();
|
||||
|
||||
protected void links(final Linked linked, final JsonGenerator jgen) throws IOException {
|
||||
final Map<String, List<String>> entitySetLinks = new HashMap<String, List<String>>();
|
||||
for (Link link : linked.getNavigationLinks()) {
|
||||
ODataLinkType type = null;
|
||||
try {
|
||||
type = ODataLinkType.fromString(version, link.getRel(), link.getType());
|
||||
} catch (IllegalArgumentException e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
if (type == ODataLinkType.ENTITY_SET_NAVIGATION) {
|
||||
final List<String> uris;
|
||||
if (entitySetLinks.containsKey(link.getTitle())) {
|
||||
uris = entitySetLinks.get(link.getTitle());
|
||||
} else {
|
||||
uris = new ArrayList<String>();
|
||||
entitySetLinks.put(link.getTitle(), uris);
|
||||
}
|
||||
uris.add(link.getHref());
|
||||
} else {
|
||||
if (StringUtils.isNotBlank(link.getHref())) {
|
||||
jgen.writeStringField(link.getTitle() + Constants.JSON_BIND_LINK_SUFFIX, link.getHref());
|
||||
}
|
||||
}
|
||||
|
||||
if (link.getInlineEntry() != null) {
|
||||
jgen.writeObjectField(link.getTitle(), link.getInlineEntry());
|
||||
} else if (link.getInlineFeed() != null) {
|
||||
jgen.writeArrayFieldStart(link.getTitle());
|
||||
for (Entry subEntry : link.getInlineFeed().getEntries()) {
|
||||
jgen.writeObject(subEntry);
|
||||
}
|
||||
jgen.writeEndArray();
|
||||
}
|
||||
}
|
||||
for (Map.Entry<String, List<String>> entitySetLink : entitySetLinks.entrySet()) {
|
||||
jgen.writeArrayFieldStart(entitySetLink.getKey() + Constants.JSON_BIND_LINK_SUFFIX);
|
||||
for (String uri : entitySetLink.getValue()) {
|
||||
jgen.writeString(uri);
|
||||
}
|
||||
jgen.writeEndArray();
|
||||
}
|
||||
}
|
||||
|
||||
private void collection(final JsonGenerator jgen, final String itemType, final CollectionValue value)
|
||||
throws IOException {
|
||||
|
||||
|
@ -85,6 +139,9 @@ abstract class AbstractJsonSerializer<T> extends ODataJacksonSerializer<T> {
|
|||
for (Property property : value.asComplex().get()) {
|
||||
property(jgen, property, property.getName());
|
||||
}
|
||||
if (value.isLinkedComplex()) {
|
||||
links(value.asLinkedComplex(), jgen);
|
||||
}
|
||||
jgen.writeEndObject();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.apache.olingo.commons.api.data.CollectionValue;
|
|||
import org.apache.olingo.commons.api.data.ComplexValue;
|
||||
import org.apache.olingo.commons.api.data.EnumValue;
|
||||
import org.apache.olingo.commons.api.data.GeospatialValue;
|
||||
import org.apache.olingo.commons.api.data.LinkedComplexValue;
|
||||
import org.apache.olingo.commons.api.data.NullValue;
|
||||
import org.apache.olingo.commons.api.data.PrimitiveValue;
|
||||
import org.apache.olingo.commons.api.data.Value;
|
||||
|
@ -57,6 +58,11 @@ public abstract class AbstractValue implements Value {
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLinkedComplex() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCollection() {
|
||||
return false;
|
||||
|
@ -87,6 +93,11 @@ public abstract class AbstractValue implements Value {
|
|||
return isComplex() ? (ComplexValue) this : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LinkedComplexValue asLinkedComplex() {
|
||||
return isLinkedComplex() ? (LinkedComplexValue) this : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CollectionValue asCollection() {
|
||||
return isCollection() ? (CollectionValue) this : null;
|
||||
|
|
|
@ -29,27 +29,264 @@ import javax.xml.stream.XMLStreamException;
|
|||
import javax.xml.stream.events.Attribute;
|
||||
import javax.xml.stream.events.StartElement;
|
||||
import javax.xml.stream.events.XMLEvent;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.olingo.commons.api.Constants;
|
||||
import org.apache.olingo.commons.api.data.CollectionValue;
|
||||
import org.apache.olingo.commons.api.data.Value;
|
||||
import org.apache.olingo.commons.api.domain.ODataOperation;
|
||||
import org.apache.olingo.commons.api.domain.ODataPropertyType;
|
||||
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
|
||||
import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
|
||||
import org.apache.olingo.commons.api.format.ContentType;
|
||||
import org.apache.olingo.commons.core.data.v3.XMLLinkCollectionImpl;
|
||||
import org.apache.olingo.commons.core.edm.EdmTypeInfo;
|
||||
|
||||
public class AtomDeserializer extends AbstractAtomDealer {
|
||||
|
||||
private static final XMLInputFactory FACTORY = XMLInputFactory.newInstance();
|
||||
|
||||
private final AtomPropertyDeserializer propDeserializer;
|
||||
private final AtomGeoValueDeserializer geoDeserializer;
|
||||
|
||||
public AtomDeserializer(final ODataServiceVersion version) {
|
||||
super(version);
|
||||
this.propDeserializer = new AtomPropertyDeserializer(version);
|
||||
this.geoDeserializer = new AtomGeoValueDeserializer();
|
||||
}
|
||||
|
||||
private Value fromPrimitive(final XMLEventReader reader, final StartElement start,
|
||||
final EdmTypeInfo typeInfo) throws XMLStreamException {
|
||||
|
||||
Value value = null;
|
||||
|
||||
boolean foundEndProperty = false;
|
||||
while (reader.hasNext() && !foundEndProperty) {
|
||||
final XMLEvent event = reader.nextEvent();
|
||||
|
||||
if (event.isStartElement() && typeInfo != null && typeInfo.getPrimitiveTypeKind().isGeospatial()) {
|
||||
final EdmPrimitiveTypeKind geoType = EdmPrimitiveTypeKind.valueOfFQN(
|
||||
version, typeInfo.getFullQualifiedName().toString());
|
||||
value = new GeospatialValueImpl(this.geoDeserializer.deserialize(reader, event.asStartElement(), geoType));
|
||||
}
|
||||
|
||||
if (event.isCharacters() && !event.asCharacters().isWhiteSpace()
|
||||
&& (typeInfo == null || !typeInfo.getPrimitiveTypeKind().isGeospatial())) {
|
||||
|
||||
value = new PrimitiveValueImpl(event.asCharacters().getData());
|
||||
}
|
||||
|
||||
if (event.isEndElement() && start.getName().equals(event.asEndElement().getName())) {
|
||||
foundEndProperty = true;
|
||||
}
|
||||
}
|
||||
|
||||
return value == null ? new PrimitiveValueImpl(StringUtils.EMPTY) : value;
|
||||
}
|
||||
|
||||
private Value fromComplexOrEnum(final XMLEventReader reader, final StartElement start)
|
||||
throws XMLStreamException {
|
||||
|
||||
Value value = null;
|
||||
|
||||
boolean foundEndProperty = false;
|
||||
while (reader.hasNext() && !foundEndProperty) {
|
||||
final XMLEvent event = reader.nextEvent();
|
||||
|
||||
if (event.isStartElement()) {
|
||||
if (value == null) {
|
||||
value = version.compareTo(ODataServiceVersion.V40) < 0
|
||||
? new ComplexValueImpl()
|
||||
: new LinkedComplexValueImpl();
|
||||
}
|
||||
|
||||
if (Constants.QNAME_ATOM_ELEM_LINK.equals(event.asStartElement().getName())) {
|
||||
final LinkImpl link = new LinkImpl();
|
||||
final Attribute rel = event.asStartElement().getAttributeByName(QName.valueOf(Constants.ATTR_REL));
|
||||
if (rel != null) {
|
||||
link.setRel(rel.getValue());
|
||||
}
|
||||
final Attribute title = event.asStartElement().getAttributeByName(QName.valueOf(Constants.ATTR_TITLE));
|
||||
if (title != null) {
|
||||
link.setTitle(title.getValue());
|
||||
}
|
||||
final Attribute href = event.asStartElement().getAttributeByName(QName.valueOf(Constants.ATTR_HREF));
|
||||
if (href != null) {
|
||||
link.setHref(href.getValue());
|
||||
}
|
||||
final Attribute type = event.asStartElement().getAttributeByName(QName.valueOf(Constants.ATTR_TYPE));
|
||||
if (type != null) {
|
||||
link.setType(type.getValue());
|
||||
}
|
||||
|
||||
if (link.getRel().startsWith(
|
||||
version.getNamespaceMap().get(ODataServiceVersion.NAVIGATION_LINK_REL))) {
|
||||
|
||||
value.asLinkedComplex().getNavigationLinks().add(link);
|
||||
inline(reader, event.asStartElement(), link);
|
||||
} else if (link.getRel().startsWith(
|
||||
version.getNamespaceMap().get(ODataServiceVersion.ASSOCIATION_LINK_REL))) {
|
||||
|
||||
value.asLinkedComplex().getAssociationLinks().add(link);
|
||||
}
|
||||
} else {
|
||||
value.asComplex().get().add(property(reader, event.asStartElement()));
|
||||
}
|
||||
}
|
||||
|
||||
if (event.isCharacters() && !event.asCharacters().isWhiteSpace()) {
|
||||
value = new EnumValueImpl(event.asCharacters().getData());
|
||||
}
|
||||
|
||||
if (event.isEndElement() && start.getName().equals(event.asEndElement().getName())) {
|
||||
foundEndProperty = true;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private CollectionValue fromCollection(final XMLEventReader reader, final StartElement start,
|
||||
final EdmTypeInfo typeInfo) throws XMLStreamException {
|
||||
|
||||
final CollectionValueImpl value = new CollectionValueImpl();
|
||||
|
||||
final EdmTypeInfo type = typeInfo == null
|
||||
? null
|
||||
: new EdmTypeInfo.Builder().setTypeExpression(typeInfo.getFullQualifiedName().toString()).build();
|
||||
|
||||
boolean foundEndProperty = false;
|
||||
while (reader.hasNext() && !foundEndProperty) {
|
||||
final XMLEvent event = reader.nextEvent();
|
||||
|
||||
if (event.isStartElement()) {
|
||||
switch (guessPropertyType(reader, typeInfo)) {
|
||||
case COMPLEX:
|
||||
case ENUM:
|
||||
value.get().add(fromComplexOrEnum(reader, event.asStartElement()));
|
||||
break;
|
||||
|
||||
case PRIMITIVE:
|
||||
value.get().add(fromPrimitive(reader, event.asStartElement(), type));
|
||||
break;
|
||||
|
||||
default:
|
||||
// do not add null or empty values
|
||||
}
|
||||
}
|
||||
|
||||
if (event.isEndElement() && start.getName().equals(event.asEndElement().getName())) {
|
||||
foundEndProperty = true;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private ODataPropertyType guessPropertyType(final XMLEventReader reader, final EdmTypeInfo typeInfo)
|
||||
throws XMLStreamException {
|
||||
|
||||
XMLEvent child = null;
|
||||
while (reader.hasNext() && child == null) {
|
||||
final XMLEvent event = reader.peek();
|
||||
if (event.isCharacters() && event.asCharacters().isWhiteSpace()) {
|
||||
reader.nextEvent();
|
||||
} else {
|
||||
child = event;
|
||||
}
|
||||
}
|
||||
|
||||
final ODataPropertyType type;
|
||||
if (child == null) {
|
||||
type = typeInfo == null || typeInfo.isPrimitiveType()
|
||||
? ODataPropertyType.PRIMITIVE
|
||||
: ODataPropertyType.ENUM;
|
||||
} else {
|
||||
if (child.isStartElement()) {
|
||||
if (Constants.NS_GML.equals(child.asStartElement().getName().getNamespaceURI())) {
|
||||
type = ODataPropertyType.PRIMITIVE;
|
||||
} else if (elementQName.equals(child.asStartElement().getName())) {
|
||||
type = ODataPropertyType.COLLECTION;
|
||||
} else {
|
||||
type = ODataPropertyType.COMPLEX;
|
||||
}
|
||||
} else if (child.isCharacters()) {
|
||||
type = typeInfo == null || typeInfo.isPrimitiveType()
|
||||
? ODataPropertyType.PRIMITIVE
|
||||
: ODataPropertyType.ENUM;
|
||||
} else {
|
||||
type = ODataPropertyType.EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
private AtomPropertyImpl property(final XMLEventReader reader, final StartElement start)
|
||||
throws XMLStreamException {
|
||||
|
||||
final AtomPropertyImpl property = new AtomPropertyImpl();
|
||||
|
||||
if (ODataServiceVersion.V40 == version && v4PropertyValueQName.equals(start.getName())) {
|
||||
// retrieve name from context
|
||||
final Attribute context = start.getAttributeByName(contextQName);
|
||||
if (context != null) {
|
||||
property.setName(StringUtils.substringAfterLast(context.getValue(), "/"));
|
||||
}
|
||||
} else {
|
||||
property.setName(start.getName().getLocalPart());
|
||||
}
|
||||
|
||||
final Attribute nullAttr = start.getAttributeByName(this.nullQName);
|
||||
|
||||
Value value;
|
||||
if (nullAttr == null) {
|
||||
final Attribute typeAttr = start.getAttributeByName(this.typeQName);
|
||||
final String typeAttrValue = typeAttr == null ? null : typeAttr.getValue();
|
||||
|
||||
final EdmTypeInfo typeInfo = StringUtils.isBlank(typeAttrValue)
|
||||
? null
|
||||
: new EdmTypeInfo.Builder().setTypeExpression(typeAttrValue).build();
|
||||
|
||||
if (typeInfo != null) {
|
||||
property.setType(typeInfo.getTypeExpression());
|
||||
}
|
||||
|
||||
final ODataPropertyType propType = typeInfo == null
|
||||
? guessPropertyType(reader, typeInfo)
|
||||
: typeInfo.isCollection()
|
||||
? ODataPropertyType.COLLECTION
|
||||
: typeInfo.isPrimitiveType()
|
||||
? ODataPropertyType.PRIMITIVE
|
||||
: ODataPropertyType.COMPLEX;
|
||||
|
||||
switch (propType) {
|
||||
case COLLECTION:
|
||||
value = fromCollection(reader, start, typeInfo);
|
||||
break;
|
||||
|
||||
case COMPLEX:
|
||||
value = fromComplexOrEnum(reader, start);
|
||||
break;
|
||||
|
||||
case PRIMITIVE:
|
||||
value = fromPrimitive(reader, start, typeInfo);
|
||||
break;
|
||||
|
||||
case EMPTY:
|
||||
default:
|
||||
value = new PrimitiveValueImpl(StringUtils.EMPTY);
|
||||
}
|
||||
} else {
|
||||
value = new NullValueImpl();
|
||||
}
|
||||
|
||||
property.setValue(value);
|
||||
|
||||
return property;
|
||||
}
|
||||
|
||||
private Container<AtomPropertyImpl> property(final InputStream input) throws XMLStreamException {
|
||||
final XMLEventReader reader = FACTORY.createXMLEventReader(input);
|
||||
final StartElement start = skipBeforeFirstStartElement(reader);
|
||||
return getContainer(start, propDeserializer.deserialize(reader, start));
|
||||
return getContainer(start, property(reader, start));
|
||||
}
|
||||
|
||||
private StartElement skipBeforeFirstStartElement(final XMLEventReader reader) throws XMLStreamException {
|
||||
|
@ -162,7 +399,7 @@ public class AtomDeserializer extends AbstractAtomDealer {
|
|||
final XMLEvent event = reader.nextEvent();
|
||||
|
||||
if (event.isStartElement()) {
|
||||
entry.getProperties().add(propDeserializer.deserialize(reader, event.asStartElement()));
|
||||
entry.getProperties().add(property(reader, event.asStartElement()));
|
||||
}
|
||||
|
||||
if (event.isEndElement() && start.getName().equals(event.asEndElement().getName())) {
|
||||
|
|
|
@ -1,240 +0,0 @@
|
|||
/*
|
||||
* 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.commons.core.data;
|
||||
|
||||
import javax.xml.stream.XMLEventReader;
|
||||
import javax.xml.stream.XMLStreamException;
|
||||
import javax.xml.stream.events.Attribute;
|
||||
import javax.xml.stream.events.StartElement;
|
||||
import javax.xml.stream.events.XMLEvent;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.olingo.commons.api.Constants;
|
||||
import org.apache.olingo.commons.api.data.CollectionValue;
|
||||
import org.apache.olingo.commons.api.data.Value;
|
||||
import org.apache.olingo.commons.api.domain.ODataPropertyType;
|
||||
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
|
||||
import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
|
||||
import org.apache.olingo.commons.core.edm.EdmTypeInfo;
|
||||
|
||||
class AtomPropertyDeserializer extends AbstractAtomDealer {
|
||||
|
||||
private final AtomGeoValueDeserializer geoDeserializer;
|
||||
|
||||
public AtomPropertyDeserializer(final ODataServiceVersion version) {
|
||||
super(version);
|
||||
this.geoDeserializer = new AtomGeoValueDeserializer();
|
||||
}
|
||||
|
||||
private Value fromPrimitive(final XMLEventReader reader, final StartElement start,
|
||||
final EdmTypeInfo typeInfo) throws XMLStreamException {
|
||||
|
||||
Value value = null;
|
||||
|
||||
boolean foundEndProperty = false;
|
||||
while (reader.hasNext() && !foundEndProperty) {
|
||||
final XMLEvent event = reader.nextEvent();
|
||||
|
||||
if (event.isStartElement() && typeInfo != null && typeInfo.getPrimitiveTypeKind().isGeospatial()) {
|
||||
final EdmPrimitiveTypeKind geoType = EdmPrimitiveTypeKind.valueOfFQN(
|
||||
version, typeInfo.getFullQualifiedName().toString());
|
||||
value = new GeospatialValueImpl(this.geoDeserializer.deserialize(reader, event.asStartElement(), geoType));
|
||||
}
|
||||
|
||||
if (event.isCharacters() && !event.asCharacters().isWhiteSpace()
|
||||
&& (typeInfo == null || !typeInfo.getPrimitiveTypeKind().isGeospatial())) {
|
||||
|
||||
value = new PrimitiveValueImpl(event.asCharacters().getData());
|
||||
}
|
||||
|
||||
if (event.isEndElement() && start.getName().equals(event.asEndElement().getName())) {
|
||||
foundEndProperty = true;
|
||||
}
|
||||
}
|
||||
|
||||
return value == null ? new PrimitiveValueImpl(StringUtils.EMPTY) : value;
|
||||
}
|
||||
|
||||
private Value fromComplexOrEnum(final XMLEventReader reader, final StartElement start)
|
||||
throws XMLStreamException {
|
||||
|
||||
Value value = null;
|
||||
|
||||
boolean foundEndProperty = false;
|
||||
while (reader.hasNext() && !foundEndProperty) {
|
||||
final XMLEvent event = reader.nextEvent();
|
||||
|
||||
if (event.isStartElement()) {
|
||||
if (value == null) {
|
||||
value = new ComplexValueImpl();
|
||||
}
|
||||
value.asComplex().get().add(deserialize(reader, event.asStartElement()));
|
||||
}
|
||||
|
||||
if (event.isCharacters() && !event.asCharacters().isWhiteSpace()) {
|
||||
value = new EnumValueImpl(event.asCharacters().getData());
|
||||
}
|
||||
|
||||
if (event.isEndElement() && start.getName().equals(event.asEndElement().getName())) {
|
||||
foundEndProperty = true;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private CollectionValue fromCollection(final XMLEventReader reader, final StartElement start,
|
||||
final EdmTypeInfo typeInfo) throws XMLStreamException {
|
||||
|
||||
final CollectionValueImpl value = new CollectionValueImpl();
|
||||
|
||||
final EdmTypeInfo type = typeInfo == null
|
||||
? null
|
||||
: new EdmTypeInfo.Builder().setTypeExpression(typeInfo.getFullQualifiedName().toString()).build();
|
||||
|
||||
boolean foundEndProperty = false;
|
||||
while (reader.hasNext() && !foundEndProperty) {
|
||||
final XMLEvent event = reader.nextEvent();
|
||||
|
||||
if (event.isStartElement()) {
|
||||
switch (guessPropertyType(reader, typeInfo)) {
|
||||
case COMPLEX:
|
||||
case ENUM:
|
||||
value.get().add(fromComplexOrEnum(reader, event.asStartElement()));
|
||||
break;
|
||||
|
||||
case PRIMITIVE:
|
||||
value.get().add(fromPrimitive(reader, event.asStartElement(), type));
|
||||
break;
|
||||
|
||||
default:
|
||||
// do not add null or empty values
|
||||
}
|
||||
}
|
||||
|
||||
if (event.isEndElement() && start.getName().equals(event.asEndElement().getName())) {
|
||||
foundEndProperty = true;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private ODataPropertyType guessPropertyType(final XMLEventReader reader, final EdmTypeInfo typeInfo)
|
||||
throws XMLStreamException {
|
||||
|
||||
XMLEvent child = null;
|
||||
while (reader.hasNext() && child == null) {
|
||||
final XMLEvent event = reader.peek();
|
||||
if (event.isCharacters() && event.asCharacters().isWhiteSpace()) {
|
||||
reader.nextEvent();
|
||||
} else {
|
||||
child = event;
|
||||
}
|
||||
}
|
||||
|
||||
final ODataPropertyType type;
|
||||
if (child == null) {
|
||||
type = typeInfo == null || typeInfo.isPrimitiveType()
|
||||
? ODataPropertyType.PRIMITIVE
|
||||
: ODataPropertyType.ENUM;
|
||||
} else {
|
||||
if (child.isStartElement()) {
|
||||
if (Constants.NS_GML.equals(child.asStartElement().getName().getNamespaceURI())) {
|
||||
type = ODataPropertyType.PRIMITIVE;
|
||||
} else if (elementQName.equals(child.asStartElement().getName())) {
|
||||
type = ODataPropertyType.COLLECTION;
|
||||
} else {
|
||||
type = ODataPropertyType.COMPLEX;
|
||||
}
|
||||
} else if (child.isCharacters()) {
|
||||
type = typeInfo == null || typeInfo.isPrimitiveType()
|
||||
? ODataPropertyType.PRIMITIVE
|
||||
: ODataPropertyType.ENUM;
|
||||
} else {
|
||||
type = ODataPropertyType.EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
public AtomPropertyImpl deserialize(final XMLEventReader reader, final StartElement start)
|
||||
throws XMLStreamException {
|
||||
|
||||
final AtomPropertyImpl property = new AtomPropertyImpl();
|
||||
|
||||
if (ODataServiceVersion.V40 == version && v4PropertyValueQName.equals(start.getName())) {
|
||||
// retrieve name from context
|
||||
final Attribute context = start.getAttributeByName(contextQName);
|
||||
if (context != null) {
|
||||
property.setName(StringUtils.substringAfterLast(context.getValue(), "/"));
|
||||
}
|
||||
} else {
|
||||
property.setName(start.getName().getLocalPart());
|
||||
}
|
||||
|
||||
final Attribute nullAttr = start.getAttributeByName(this.nullQName);
|
||||
|
||||
Value value;
|
||||
if (nullAttr == null) {
|
||||
final Attribute typeAttr = start.getAttributeByName(this.typeQName);
|
||||
final String typeAttrValue = typeAttr == null ? null : typeAttr.getValue();
|
||||
|
||||
final EdmTypeInfo typeInfo = StringUtils.isBlank(typeAttrValue)
|
||||
? null
|
||||
: new EdmTypeInfo.Builder().setTypeExpression(typeAttrValue).build();
|
||||
|
||||
if (typeInfo != null) {
|
||||
property.setType(typeInfo.getTypeExpression());
|
||||
}
|
||||
|
||||
final ODataPropertyType propType = typeInfo == null
|
||||
? guessPropertyType(reader, typeInfo)
|
||||
: typeInfo.isCollection()
|
||||
? ODataPropertyType.COLLECTION
|
||||
: typeInfo.isPrimitiveType()
|
||||
? ODataPropertyType.PRIMITIVE
|
||||
: ODataPropertyType.COMPLEX;
|
||||
|
||||
switch (propType) {
|
||||
case COLLECTION:
|
||||
value = fromCollection(reader, start, typeInfo);
|
||||
break;
|
||||
|
||||
case COMPLEX:
|
||||
value = fromComplexOrEnum(reader, start);
|
||||
break;
|
||||
|
||||
case PRIMITIVE:
|
||||
value = fromPrimitive(reader, start, typeInfo);
|
||||
break;
|
||||
|
||||
case EMPTY:
|
||||
default:
|
||||
value = new PrimitiveValueImpl(StringUtils.EMPTY);
|
||||
}
|
||||
} else {
|
||||
value = new NullValueImpl();
|
||||
}
|
||||
|
||||
property.setValue(value);
|
||||
|
||||
return property;
|
||||
}
|
||||
}
|
|
@ -1,116 +0,0 @@
|
|||
/*
|
||||
* 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.commons.core.data;
|
||||
|
||||
import javax.xml.stream.XMLStreamException;
|
||||
import javax.xml.stream.XMLStreamWriter;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.olingo.commons.api.Constants;
|
||||
import org.apache.olingo.commons.api.data.CollectionValue;
|
||||
import org.apache.olingo.commons.api.data.Property;
|
||||
import org.apache.olingo.commons.api.data.Value;
|
||||
import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
|
||||
import org.apache.olingo.commons.core.edm.EdmTypeInfo;
|
||||
|
||||
class AtomPropertySerializer extends AbstractAtomDealer {
|
||||
|
||||
private final AtomGeoValueSerializer geoSerializer;
|
||||
|
||||
public AtomPropertySerializer(final ODataServiceVersion version) {
|
||||
super(version);
|
||||
this.geoSerializer = new AtomGeoValueSerializer();
|
||||
}
|
||||
|
||||
private void collection(final XMLStreamWriter writer, final CollectionValue value) throws XMLStreamException {
|
||||
for (Value item : value.get()) {
|
||||
if (version == ODataServiceVersion.V30) {
|
||||
writer.writeStartElement(Constants.PREFIX_DATASERVICES, Constants.ELEM_ELEMENT,
|
||||
version.getNamespaceMap().get(ODataServiceVersion.NS_DATASERVICES));
|
||||
} else {
|
||||
writer.writeStartElement(Constants.PREFIX_METADATA, Constants.ELEM_ELEMENT,
|
||||
version.getNamespaceMap().get(ODataServiceVersion.NS_METADATA));
|
||||
}
|
||||
value(writer, item);
|
||||
writer.writeEndElement();
|
||||
}
|
||||
}
|
||||
|
||||
private void value(final XMLStreamWriter writer, final Value value) throws XMLStreamException {
|
||||
if (value.isPrimitive()) {
|
||||
writer.writeCharacters(value.asPrimitive().get());
|
||||
} else if (value.isEnum()) {
|
||||
writer.writeCharacters(value.asEnum().get());
|
||||
} else if (value.isGeospatial()) {
|
||||
this.geoSerializer.serialize(writer, value.asGeospatial().get());
|
||||
} else if (value.isCollection()) {
|
||||
collection(writer, value.asCollection());
|
||||
} else if (value.isComplex()) {
|
||||
for (Property property : value.asComplex().get()) {
|
||||
property(writer, property, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void property(final XMLStreamWriter writer, final Property property, final boolean standalone)
|
||||
throws XMLStreamException {
|
||||
|
||||
if (version == ODataServiceVersion.V40 && standalone) {
|
||||
writer.writeStartElement(Constants.PREFIX_METADATA, Constants.VALUE,
|
||||
version.getNamespaceMap().get(ODataServiceVersion.NS_DATASERVICES));
|
||||
} else {
|
||||
writer.writeStartElement(Constants.PREFIX_DATASERVICES, property.getName(),
|
||||
version.getNamespaceMap().get(ODataServiceVersion.NS_DATASERVICES));
|
||||
}
|
||||
|
||||
if (standalone) {
|
||||
namespaces(writer);
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(property.getType())) {
|
||||
String type = property.getType();
|
||||
if (version == ODataServiceVersion.V40) {
|
||||
final EdmTypeInfo typeInfo = new EdmTypeInfo.Builder().setTypeExpression(property.getType()).build();
|
||||
if (typeInfo.isPrimitiveType()) {
|
||||
if (typeInfo.isCollection()) {
|
||||
type = "#Collection(" + typeInfo.getFullQualifiedName().getName() + ")";
|
||||
} else {
|
||||
type = typeInfo.getFullQualifiedName().getName();
|
||||
}
|
||||
} else {
|
||||
type = "#" + property.getType();
|
||||
}
|
||||
}
|
||||
writer.writeAttribute(Constants.PREFIX_METADATA, version.getNamespaceMap().get(ODataServiceVersion.NS_METADATA),
|
||||
Constants.ATTR_TYPE, type);
|
||||
}
|
||||
|
||||
if (property.getValue().isNull()) {
|
||||
writer.writeAttribute(Constants.PREFIX_METADATA, version.getNamespaceMap().get(ODataServiceVersion.NS_METADATA),
|
||||
Constants.ATTR_NULL, Boolean.TRUE.toString());
|
||||
} else {
|
||||
value(writer, property.getValue());
|
||||
}
|
||||
|
||||
writer.writeEndElement();
|
||||
}
|
||||
|
||||
public void property(final XMLStreamWriter writer, final Property property) throws XMLStreamException {
|
||||
property(writer, property, true);
|
||||
}
|
||||
}
|
|
@ -27,22 +27,106 @@ import javax.xml.stream.XMLStreamException;
|
|||
import javax.xml.stream.XMLStreamWriter;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.olingo.commons.api.Constants;
|
||||
import org.apache.olingo.commons.api.data.CollectionValue;
|
||||
import org.apache.olingo.commons.api.data.Entry;
|
||||
import org.apache.olingo.commons.api.data.Feed;
|
||||
import org.apache.olingo.commons.api.data.Link;
|
||||
import org.apache.olingo.commons.api.data.Property;
|
||||
import org.apache.olingo.commons.api.data.Value;
|
||||
import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
|
||||
import org.apache.olingo.commons.api.format.ContentType;
|
||||
import org.apache.olingo.commons.core.edm.EdmTypeInfo;
|
||||
|
||||
public class AtomSerializer extends AbstractAtomDealer {
|
||||
|
||||
private static final XMLOutputFactory FACTORY = XMLOutputFactory.newInstance();
|
||||
|
||||
private final AtomPropertySerializer propSerializer;
|
||||
private final AtomGeoValueSerializer geoSerializer;
|
||||
|
||||
public AtomSerializer(final ODataServiceVersion version) {
|
||||
super(version);
|
||||
this.propSerializer = new AtomPropertySerializer(version);
|
||||
this.geoSerializer = new AtomGeoValueSerializer();
|
||||
}
|
||||
|
||||
private void collection(final XMLStreamWriter writer, final CollectionValue value) throws XMLStreamException {
|
||||
for (Value item : value.get()) {
|
||||
if (version.compareTo(ODataServiceVersion.V40) < 0) {
|
||||
writer.writeStartElement(Constants.PREFIX_DATASERVICES, Constants.ELEM_ELEMENT,
|
||||
version.getNamespaceMap().get(ODataServiceVersion.NS_DATASERVICES));
|
||||
} else {
|
||||
writer.writeStartElement(Constants.PREFIX_METADATA, Constants.ELEM_ELEMENT,
|
||||
version.getNamespaceMap().get(ODataServiceVersion.NS_METADATA));
|
||||
}
|
||||
value(writer, item);
|
||||
writer.writeEndElement();
|
||||
}
|
||||
}
|
||||
|
||||
private void value(final XMLStreamWriter writer, final Value value) throws XMLStreamException {
|
||||
if (value.isPrimitive()) {
|
||||
writer.writeCharacters(value.asPrimitive().get());
|
||||
} else if (value.isEnum()) {
|
||||
writer.writeCharacters(value.asEnum().get());
|
||||
} else if (value.isGeospatial()) {
|
||||
this.geoSerializer.serialize(writer, value.asGeospatial().get());
|
||||
} else if (value.isCollection()) {
|
||||
collection(writer, value.asCollection());
|
||||
} else if (value.isComplex()) {
|
||||
for (Property property : value.asComplex().get()) {
|
||||
property(writer, property, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void property(final XMLStreamWriter writer, final Property property, final boolean standalone)
|
||||
throws XMLStreamException {
|
||||
|
||||
if (version.compareTo(ODataServiceVersion.V40) >= 0 && standalone) {
|
||||
writer.writeStartElement(Constants.PREFIX_METADATA, Constants.VALUE,
|
||||
version.getNamespaceMap().get(ODataServiceVersion.NS_DATASERVICES));
|
||||
} else {
|
||||
writer.writeStartElement(Constants.PREFIX_DATASERVICES, property.getName(),
|
||||
version.getNamespaceMap().get(ODataServiceVersion.NS_DATASERVICES));
|
||||
}
|
||||
|
||||
if (standalone) {
|
||||
namespaces(writer);
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(property.getType())) {
|
||||
String type = property.getType();
|
||||
if (version.compareTo(ODataServiceVersion.V40) >= 0) {
|
||||
final EdmTypeInfo typeInfo = new EdmTypeInfo.Builder().setTypeExpression(property.getType()).build();
|
||||
if (typeInfo.isPrimitiveType()) {
|
||||
if (typeInfo.isCollection()) {
|
||||
type = "#Collection(" + typeInfo.getFullQualifiedName().getName() + ")";
|
||||
} else {
|
||||
type = typeInfo.getFullQualifiedName().getName();
|
||||
}
|
||||
} else {
|
||||
type = "#" + property.getType();
|
||||
}
|
||||
}
|
||||
writer.writeAttribute(Constants.PREFIX_METADATA, version.getNamespaceMap().get(ODataServiceVersion.NS_METADATA),
|
||||
Constants.ATTR_TYPE, type);
|
||||
}
|
||||
|
||||
if (property.getValue().isNull()) {
|
||||
writer.writeAttribute(Constants.PREFIX_METADATA, version.getNamespaceMap().get(ODataServiceVersion.NS_METADATA),
|
||||
Constants.ATTR_NULL, Boolean.TRUE.toString());
|
||||
} else {
|
||||
value(writer, property.getValue());
|
||||
if (property.getValue().isLinkedComplex()) {
|
||||
links(writer, property.getValue().asLinkedComplex().getAssociationLinks());
|
||||
links(writer, property.getValue().asLinkedComplex().getNavigationLinks());
|
||||
}
|
||||
}
|
||||
|
||||
writer.writeEndElement();
|
||||
}
|
||||
|
||||
private void property(final XMLStreamWriter writer, final Property property) throws XMLStreamException {
|
||||
property(writer, property, true);
|
||||
}
|
||||
|
||||
private void startDocument(final XMLStreamWriter writer, final String rootElement) throws XMLStreamException {
|
||||
|
@ -59,7 +143,7 @@ public class AtomSerializer extends AbstractAtomDealer {
|
|||
|
||||
writer.writeStartDocument();
|
||||
|
||||
propSerializer.property(writer, property);
|
||||
property(writer, property);
|
||||
|
||||
writer.writeEndDocument();
|
||||
writer.flush();
|
||||
|
@ -122,7 +206,7 @@ public class AtomSerializer extends AbstractAtomDealer {
|
|||
|
||||
private void properties(final XMLStreamWriter writer, final List<Property> properties) throws XMLStreamException {
|
||||
for (Property property : properties) {
|
||||
propSerializer.property(writer, property, false);
|
||||
property(writer, property, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,11 +21,8 @@ package org.apache.olingo.commons.core.data;
|
|||
import com.fasterxml.jackson.core.JsonParseException;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.ObjectCodec;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
|
@ -47,47 +44,11 @@ import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
|
|||
*/
|
||||
public class JSONEntryDeserializer extends AbstractJsonDeserializer<JSONEntryImpl> {
|
||||
|
||||
private String getTitle(final Map.Entry<String, JsonNode> entry) {
|
||||
return entry.getKey().substring(0, entry.getKey().indexOf('@'));
|
||||
}
|
||||
|
||||
private String setInline(final String name, final String suffix, final ObjectNode tree,
|
||||
final ObjectCodec codec, final LinkImpl link) throws IOException {
|
||||
|
||||
final String entryNamePrefix = name.substring(0, name.indexOf(suffix));
|
||||
if (tree.has(entryNamePrefix)) {
|
||||
final JsonNode inline = tree.path(entryNamePrefix);
|
||||
|
||||
if (inline instanceof ObjectNode) {
|
||||
link.setType(ODataLinkType.ENTITY_NAVIGATION.toString());
|
||||
|
||||
link.setInlineEntry(inline.traverse(codec).<Container<JSONEntryImpl>>readValueAs(
|
||||
new TypeReference<JSONEntryImpl>() {
|
||||
}).getObject());
|
||||
}
|
||||
|
||||
if (inline instanceof ArrayNode) {
|
||||
link.setType(ODataLinkType.ENTITY_SET_NAVIGATION.toString());
|
||||
|
||||
final JSONFeedImpl feed = new JSONFeedImpl();
|
||||
final Iterator<JsonNode> entries = ((ArrayNode) inline).elements();
|
||||
while (entries.hasNext()) {
|
||||
feed.getEntries().add(entries.next().traverse(codec).<Container<JSONEntryImpl>>readValuesAs(
|
||||
new TypeReference<JSONEntryImpl>() {
|
||||
}).next().getObject());
|
||||
}
|
||||
|
||||
link.setInlineFeed(feed);
|
||||
}
|
||||
}
|
||||
return entryNamePrefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Container<JSONEntryImpl> doDeserialize(final JsonParser parser, final DeserializationContext ctxt)
|
||||
throws IOException, JsonProcessingException {
|
||||
|
||||
final ObjectNode tree = (ObjectNode) parser.getCodec().readTree(parser);
|
||||
final ObjectNode tree = parser.getCodec().readTree(parser);
|
||||
|
||||
if (tree.has(Constants.VALUE) && tree.get(Constants.VALUE).isArray()) {
|
||||
throw new JsonParseException("Expected OData Entity, found EntitySet", parser.getCurrentLocation());
|
||||
|
@ -173,35 +134,8 @@ public class JSONEntryDeserializer extends AbstractJsonDeserializer<JSONEntryImp
|
|||
for (final Iterator<Map.Entry<String, JsonNode>> itor = tree.fields(); itor.hasNext();) {
|
||||
final Map.Entry<String, JsonNode> field = itor.next();
|
||||
|
||||
if (field.getKey().endsWith(jsonNavigationLink)) {
|
||||
final LinkImpl link = new LinkImpl();
|
||||
link.setTitle(getTitle(field));
|
||||
link.setRel(version.getNamespaceMap().get(ODataServiceVersion.NAVIGATION_LINK_REL) + getTitle(field));
|
||||
|
||||
if (field.getValue().isValueNode()) {
|
||||
link.setHref(field.getValue().textValue());
|
||||
link.setType(ODataLinkType.ENTITY_NAVIGATION.toString());
|
||||
}
|
||||
// NOTE: this should be expected to happen, but it isn't - at least up to OData 4.0
|
||||
/* if (field.getValue().isArray()) {
|
||||
* link.setHref(field.getValue().asText());
|
||||
* link.setType(ODataLinkType.ENTITY_SET_NAVIGATION.toString());
|
||||
* } */
|
||||
|
||||
entry.getNavigationLinks().add(link);
|
||||
|
||||
toRemove.add(field.getKey());
|
||||
toRemove.add(setInline(field.getKey(), jsonNavigationLink, tree, parser.getCodec(), link));
|
||||
} else if (field.getKey().endsWith(jsonAssociationLink)) {
|
||||
final LinkImpl link = new LinkImpl();
|
||||
link.setTitle(getTitle(field));
|
||||
link.setRel(version.getNamespaceMap().get(ODataServiceVersion.ASSOCIATION_LINK_REL) + getTitle(field));
|
||||
link.setHref(field.getValue().textValue());
|
||||
link.setType(ODataLinkType.ASSOCIATION.toString());
|
||||
entry.getAssociationLinks().add(link);
|
||||
|
||||
toRemove.add(field.getKey());
|
||||
} else if (field.getKey().endsWith(getJSONAnnotation(jsonMediaEditLink))) {
|
||||
links(field, entry, toRemove, tree, parser.getCodec());
|
||||
if (field.getKey().endsWith(getJSONAnnotation(jsonMediaEditLink))) {
|
||||
final LinkImpl link = new LinkImpl();
|
||||
link.setTitle(getTitle(field));
|
||||
link.setRel(version.getNamespaceMap().get(ODataServiceVersion.MEDIA_EDIT_LINK_REL) + getTitle(field));
|
||||
|
@ -251,7 +185,7 @@ public class JSONEntryDeserializer extends AbstractJsonDeserializer<JSONEntryImp
|
|||
property.setType(type);
|
||||
type = null;
|
||||
|
||||
value(property, field.getValue());
|
||||
value(property, field.getValue(), parser.getCodec());
|
||||
entry.getProperties().add(property);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,16 +22,9 @@ import com.fasterxml.jackson.core.JsonGenerator;
|
|||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.olingo.commons.api.Constants;
|
||||
import org.apache.olingo.commons.api.data.Entry;
|
||||
import org.apache.olingo.commons.api.data.Link;
|
||||
import org.apache.olingo.commons.api.data.Property;
|
||||
import org.apache.olingo.commons.api.domain.ODataLinkType;
|
||||
import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
|
||||
|
||||
/**
|
||||
|
@ -49,49 +42,12 @@ public class JSONEntrySerializer extends AbstractJsonSerializer<JSONEntryImpl> {
|
|||
jgen.writeStringField(version.getJSONMap().get(ODataServiceVersion.JSON_ID), entry.getId());
|
||||
}
|
||||
|
||||
final Map<String, List<String>> entitySetLinks = new HashMap<String, List<String>>();
|
||||
|
||||
for (Link link : entry.getNavigationLinks()) {
|
||||
ODataLinkType type = null;
|
||||
try {
|
||||
type = ODataLinkType.fromString(version, link.getRel(), link.getType());
|
||||
} catch (IllegalArgumentException e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
if (type == ODataLinkType.ENTITY_SET_NAVIGATION) {
|
||||
final List<String> uris;
|
||||
if (entitySetLinks.containsKey(link.getTitle())) {
|
||||
uris = entitySetLinks.get(link.getTitle());
|
||||
} else {
|
||||
uris = new ArrayList<String>();
|
||||
entitySetLinks.put(link.getTitle(), uris);
|
||||
}
|
||||
uris.add(link.getHref());
|
||||
} else {
|
||||
if (StringUtils.isNotBlank(link.getHref())) {
|
||||
jgen.writeStringField(link.getTitle() + Constants.JSON_BIND_LINK_SUFFIX, link.getHref());
|
||||
}
|
||||
}
|
||||
|
||||
if (link.getInlineEntry() != null) {
|
||||
jgen.writeObjectField(link.getTitle(), link.getInlineEntry());
|
||||
} else if (link.getInlineFeed() != null) {
|
||||
jgen.writeArrayFieldStart(link.getTitle());
|
||||
for (Entry subEntry : link.getInlineFeed().getEntries()) {
|
||||
jgen.writeObject(subEntry);
|
||||
}
|
||||
jgen.writeEndArray();
|
||||
}
|
||||
}
|
||||
for (Map.Entry<String, List<String>> entitySetLink : entitySetLinks.entrySet()) {
|
||||
jgen.writeArrayFieldStart(entitySetLink.getKey() + Constants.JSON_BIND_LINK_SUFFIX);
|
||||
for (String uri : entitySetLink.getValue()) {
|
||||
jgen.writeString(uri);
|
||||
}
|
||||
jgen.writeEndArray();
|
||||
for (Property property : entry.getProperties()) {
|
||||
property(jgen, property, property.getName());
|
||||
}
|
||||
|
||||
links(entry, jgen);
|
||||
|
||||
for (Link link : entry.getMediaEditLinks()) {
|
||||
if (link.getTitle() == null) {
|
||||
jgen.writeStringField(version.getJSONMap().get(ODataServiceVersion.JSON_MEDIAEDIT_LINK), link.getHref());
|
||||
|
@ -109,10 +65,6 @@ public class JSONEntrySerializer extends AbstractJsonSerializer<JSONEntryImpl> {
|
|||
}
|
||||
}
|
||||
|
||||
for (Property property : entry.getProperties()) {
|
||||
property(jgen, property, property.getName());
|
||||
}
|
||||
|
||||
jgen.writeEndObject();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,7 +73,7 @@ public class JSONPropertyDeserializer extends AbstractJsonDeserializer<JSONPrope
|
|||
}
|
||||
|
||||
if (property.getValue() == null) {
|
||||
value(property, tree.has(Constants.VALUE) ? tree.get(Constants.VALUE) : tree);
|
||||
value(property, tree.has(Constants.VALUE) ? tree.get(Constants.VALUE) : tree, parser.getCodec());
|
||||
}
|
||||
|
||||
return new Container<JSONPropertyImpl>(contextURL, metadataETag, property);
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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.commons.core.data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.apache.olingo.commons.api.data.Link;
|
||||
import org.apache.olingo.commons.api.data.LinkedComplexValue;
|
||||
|
||||
public class LinkedComplexValueImpl extends ComplexValueImpl implements LinkedComplexValue {
|
||||
|
||||
private final List<Link> associationLinks = new ArrayList<Link>();
|
||||
|
||||
private final List<Link> navigationLinks = new ArrayList<Link>();
|
||||
|
||||
@Override
|
||||
public boolean isLinkedComplex() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Link> getAssociationLinks() {
|
||||
return associationLinks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Link> getNavigationLinks() {
|
||||
return navigationLinks;
|
||||
}
|
||||
|
||||
}
|
|
@ -18,6 +18,7 @@
|
|||
*/
|
||||
package org.apache.olingo.commons.core.domain.v4;
|
||||
|
||||
import org.apache.olingo.commons.api.domain.v4.ODataLinkedComplexValue;
|
||||
import org.apache.olingo.commons.api.domain.v4.ODataEnumValue;
|
||||
import org.apache.olingo.commons.api.domain.v4.ODataValue;
|
||||
import org.apache.olingo.commons.core.domain.AbstractODataCollectionValue;
|
||||
|
@ -39,4 +40,14 @@ public class ODataCollectionValueImpl extends AbstractODataCollectionValue<OData
|
|||
public ODataEnumValue asEnum() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLinkedComplex() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ODataLinkedComplexValue asLinkedComplex() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,15 +18,28 @@
|
|||
*/
|
||||
package org.apache.olingo.commons.core.domain.v4;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.apache.olingo.commons.api.domain.ODataLink;
|
||||
import org.apache.olingo.commons.api.domain.v4.ODataLinkedComplexValue;
|
||||
import org.apache.olingo.commons.api.domain.v4.ODataEnumValue;
|
||||
import org.apache.olingo.commons.api.domain.v4.ODataProperty;
|
||||
import org.apache.olingo.commons.api.domain.v4.ODataValue;
|
||||
import org.apache.olingo.commons.core.domain.AbstractODataComplexValue;
|
||||
|
||||
public class ODataComplexValueImpl extends AbstractODataComplexValue<ODataProperty> implements ODataValue {
|
||||
public class ODataComplexValueImpl extends AbstractODataComplexValue<ODataProperty> implements ODataLinkedComplexValue {
|
||||
|
||||
private static final long serialVersionUID = 1143925901934898802L;
|
||||
|
||||
/**
|
||||
* Navigation links (might contain in-line entities or feeds).
|
||||
*/
|
||||
private final List<ODataLink> navigationLinks = new ArrayList<ODataLink>();
|
||||
|
||||
/**
|
||||
* Association links.
|
||||
*/
|
||||
private final List<ODataLink> associationLinks = new ArrayList<ODataLink>();
|
||||
|
||||
public ODataComplexValueImpl(final String typeName) {
|
||||
super(typeName);
|
||||
}
|
||||
|
@ -41,4 +54,73 @@ public class ODataComplexValueImpl extends AbstractODataComplexValue<ODataProper
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLinkedComplex() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ODataLinkedComplexValue asLinkedComplex() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addLink(final ODataLink link) {
|
||||
boolean result = false;
|
||||
|
||||
switch (link.getType()) {
|
||||
case ASSOCIATION:
|
||||
result = associationLinks.contains(link) ? false : associationLinks.add(link);
|
||||
break;
|
||||
|
||||
case ENTITY_NAVIGATION:
|
||||
case ENTITY_SET_NAVIGATION:
|
||||
result = navigationLinks.contains(link) ? false : navigationLinks.add(link);
|
||||
break;
|
||||
|
||||
case MEDIA_EDIT:
|
||||
throw new IllegalArgumentException("Complex values cannot have media links!");
|
||||
|
||||
default:
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeLink(final ODataLink link) {
|
||||
return associationLinks.remove(link) || navigationLinks.remove(link);
|
||||
}
|
||||
|
||||
private ODataLink getLink(final List<ODataLink> links, final String name) {
|
||||
ODataLink result = null;
|
||||
for (ODataLink link : links) {
|
||||
if (name.equals(link.getName())) {
|
||||
result = link;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ODataLink getNavigationLink(final String name) {
|
||||
return getLink(navigationLinks, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ODataLink> getNavigationLinks() {
|
||||
return navigationLinks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ODataLink getAssociationLink(final String name) {
|
||||
return getLink(associationLinks, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ODataLink> getAssociationLinks() {
|
||||
return associationLinks;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
package org.apache.olingo.commons.core.domain.v4;
|
||||
|
||||
import org.apache.olingo.commons.api.domain.AbstractODataValue;
|
||||
import org.apache.olingo.commons.api.domain.v4.ODataLinkedComplexValue;
|
||||
import org.apache.olingo.commons.api.domain.v4.ODataEnumValue;
|
||||
|
||||
public class ODataEnumValueImpl extends AbstractODataValue implements ODataEnumValue {
|
||||
|
@ -47,4 +48,13 @@ public class ODataEnumValueImpl extends AbstractODataValue implements ODataEnumV
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLinkedComplex() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ODataLinkedComplexValue asLinkedComplex() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ import org.apache.olingo.commons.api.domain.v4.ODataEntitySet;
|
|||
import org.apache.olingo.commons.api.domain.v4.ODataObjectFactory;
|
||||
import org.apache.olingo.commons.api.domain.v4.ODataEntity;
|
||||
import org.apache.olingo.commons.api.domain.v4.ODataEnumValue;
|
||||
import org.apache.olingo.commons.api.domain.v4.ODataLinkedComplexValue;
|
||||
import org.apache.olingo.commons.api.domain.v4.ODataProperty;
|
||||
import org.apache.olingo.commons.api.domain.v4.ODataValue;
|
||||
import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
|
||||
|
@ -75,6 +76,11 @@ public class ODataObjectFactoryImpl extends AbstractODataObjectFactory implement
|
|||
return new ODataComplexValueImpl(typeName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ODataLinkedComplexValue newLinkedComplexValue(final String typeName) {
|
||||
return new ODataComplexValueImpl(typeName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ODataCollectionValue<ODataValue> newCollectionValue(final String typeName) {
|
||||
return new ODataCollectionValueImpl(typeName);
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
*/
|
||||
package org.apache.olingo.commons.core.domain.v4;
|
||||
|
||||
import org.apache.olingo.commons.api.domain.v4.ODataLinkedComplexValue;
|
||||
import org.apache.olingo.commons.api.domain.v4.ODataEnumValue;
|
||||
import org.apache.olingo.commons.api.domain.v4.ODataValue;
|
||||
import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
|
||||
|
@ -53,4 +54,14 @@ public class ODataPrimitiveValueImpl extends AbstractODataPrimitiveValue impleme
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLinkedComplex() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ODataLinkedComplexValue asLinkedComplex() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ package org.apache.olingo.commons.core.domain.v4;
|
|||
import org.apache.olingo.commons.api.domain.ODataCollectionValue;
|
||||
import org.apache.olingo.commons.api.domain.ODataComplexValue;
|
||||
import org.apache.olingo.commons.api.domain.v4.ODataEnumValue;
|
||||
import org.apache.olingo.commons.api.domain.v4.ODataLinkedComplexValue;
|
||||
import org.apache.olingo.commons.api.domain.v4.ODataProperty;
|
||||
import org.apache.olingo.commons.api.domain.v4.ODataValue;
|
||||
import org.apache.olingo.commons.core.domain.AbstractODataProperty;
|
||||
|
@ -41,7 +42,9 @@ public class ODataPropertyImpl extends AbstractODataProperty implements ODataPro
|
|||
|
||||
@Override
|
||||
public ODataEnumValue getEnumValue() {
|
||||
return hasEnumValue() ? ((org.apache.olingo.commons.api.domain.v4.ODataValue) getValue()).asEnum() : null;
|
||||
return hasEnumValue()
|
||||
? ((org.apache.olingo.commons.api.domain.v4.ODataValue) getValue()).asEnum()
|
||||
: null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -49,9 +52,18 @@ public class ODataPropertyImpl extends AbstractODataProperty implements ODataPro
|
|||
return hasComplexValue() ? getValue().<ODataProperty>asComplex() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ODataLinkedComplexValue getLinkedComplexValue() {
|
||||
return hasComplexValue() && getValue() instanceof org.apache.olingo.commons.api.domain.v4.ODataValue
|
||||
? ((org.apache.olingo.commons.api.domain.v4.ODataValue) getValue()).asLinkedComplex()
|
||||
: null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ODataCollectionValue<ODataValue> getCollectionValue() {
|
||||
return hasCollectionValue() ? getValue().<ODataValue>asCollection() : null;
|
||||
return hasCollectionValue()
|
||||
? getValue().<ODataValue>asCollection()
|
||||
: null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue