| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.olingo.server.core.serializer.json; |
| |
| import static org.apache.olingo.server.core.serializer.utils.ContentTypeHelper.isODataMetadataFull; |
| import static org.apache.olingo.server.core.serializer.utils.ContentTypeHelper.isODataMetadataNone; |
| |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| |
| import org.apache.olingo.commons.api.Constants; |
| import org.apache.olingo.commons.api.data.AbstractEntityCollection; |
| import org.apache.olingo.commons.api.data.ComplexValue; |
| import org.apache.olingo.commons.api.data.ContextURL; |
| import org.apache.olingo.commons.api.data.Entity; |
| import org.apache.olingo.commons.api.data.EntityIterator; |
| import org.apache.olingo.commons.api.data.Link; |
| import org.apache.olingo.commons.api.data.Linked; |
| import org.apache.olingo.commons.api.data.Operation; |
| import org.apache.olingo.commons.api.data.Property; |
| import org.apache.olingo.commons.api.edm.EdmComplexType; |
| import org.apache.olingo.commons.api.edm.EdmEntitySet; |
| import org.apache.olingo.commons.api.edm.EdmEntityType; |
| import org.apache.olingo.commons.api.edm.EdmNavigationProperty; |
| import org.apache.olingo.commons.api.edm.EdmPrimitiveType; |
| import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException; |
| import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind; |
| import org.apache.olingo.commons.api.edm.EdmProperty; |
| import org.apache.olingo.commons.api.edm.EdmStructuredType; |
| import org.apache.olingo.commons.api.edm.EdmType; |
| import org.apache.olingo.commons.api.edm.FullQualifiedName; |
| import org.apache.olingo.commons.api.edm.constants.EdmTypeKind; |
| import org.apache.olingo.commons.api.format.ContentType; |
| import org.apache.olingo.commons.core.edm.primitivetype.EdmPrimitiveTypeFactory; |
| import org.apache.olingo.server.api.ODataServerError; |
| import org.apache.olingo.server.api.ServiceMetadata; |
| import org.apache.olingo.server.api.serializer.ComplexSerializerOptions; |
| import org.apache.olingo.server.api.serializer.EntityCollectionSerializerOptions; |
| import org.apache.olingo.server.api.serializer.EntitySerializerOptions; |
| import org.apache.olingo.server.api.serializer.PrimitiveSerializerOptions; |
| import org.apache.olingo.server.api.serializer.ReferenceCollectionSerializerOptions; |
| import org.apache.olingo.server.api.serializer.ReferenceSerializerOptions; |
| import org.apache.olingo.server.api.serializer.SerializerException; |
| import org.apache.olingo.server.api.serializer.SerializerResult; |
| import org.apache.olingo.server.api.serializer.SerializerStreamResult; |
| import org.apache.olingo.server.api.uri.UriHelper; |
| import org.apache.olingo.server.api.uri.queryoption.CountOption; |
| import org.apache.olingo.server.api.uri.queryoption.ExpandItem; |
| import org.apache.olingo.server.api.uri.queryoption.ExpandOption; |
| import org.apache.olingo.server.api.uri.queryoption.SelectOption; |
| import org.apache.olingo.server.core.ODataWritableContent; |
| import org.apache.olingo.server.core.serializer.AbstractODataSerializer; |
| import org.apache.olingo.server.core.serializer.SerializerResultImpl; |
| import org.apache.olingo.server.core.serializer.utils.CircleStreamBuffer; |
| import org.apache.olingo.server.core.serializer.utils.ContextURLBuilder; |
| import org.apache.olingo.server.core.serializer.utils.ExpandSelectHelper; |
| import org.apache.olingo.server.core.uri.UriHelperImpl; |
| |
| import com.fasterxml.jackson.core.JsonFactory; |
| import com.fasterxml.jackson.core.JsonGenerator; |
| |
| public class ODataJsonSerializer extends AbstractODataSerializer { |
| |
| private final boolean isIEEE754Compatible; |
| private final ContentType contentType; |
| |
| public ODataJsonSerializer(final ContentType contentType) { |
| this.contentType = contentType; |
| isIEEE754Compatible = isODataIEEE754Compatible(contentType); |
| } |
| |
| @Override |
| public SerializerResult serviceDocument(final ServiceMetadata metadata, final String serviceRoot) |
| throws SerializerException { |
| OutputStream outputStream = null; |
| SerializerException cachedException = null; |
| |
| try { |
| CircleStreamBuffer buffer = new CircleStreamBuffer(); |
| outputStream = buffer.getOutputStream(); |
| JsonGenerator json = new JsonFactory().createGenerator(outputStream); |
| new ServiceDocumentJsonSerializer(metadata, serviceRoot, |
| isODataMetadataNone(contentType)).writeServiceDocument(json); |
| |
| json.close(); |
| outputStream.close(); |
| return SerializerResultImpl.with().content(buffer.getInputStream()).build(); |
| } catch (final IOException e) { |
| cachedException = |
| new SerializerException(IO_EXCEPTION_TEXT, e, SerializerException.MessageKeys.IO_EXCEPTION); |
| throw cachedException; |
| } finally { |
| closeCircleStreamBufferOutput(outputStream, cachedException); |
| } |
| } |
| |
| @Override |
| public SerializerResult metadataDocument(final ServiceMetadata serviceMetadata) throws SerializerException { |
| throw new SerializerException("Metadata in JSON format not supported!", |
| SerializerException.MessageKeys.JSON_METADATA); |
| } |
| |
| @Override |
| public SerializerResult error(final ODataServerError error) throws SerializerException { |
| OutputStream outputStream = null; |
| SerializerException cachedException = null; |
| try { |
| CircleStreamBuffer buffer = new CircleStreamBuffer(); |
| outputStream = buffer.getOutputStream(); |
| JsonGenerator json = new JsonFactory().createGenerator(outputStream); |
| new ODataErrorSerializer().writeErrorDocument(json, error); |
| |
| json.close(); |
| outputStream.close(); |
| return SerializerResultImpl.with().content(buffer.getInputStream()).build(); |
| } catch (final IOException e) { |
| cachedException = |
| new SerializerException(IO_EXCEPTION_TEXT, e, SerializerException.MessageKeys.IO_EXCEPTION); |
| throw cachedException; |
| } finally { |
| closeCircleStreamBufferOutput(outputStream, cachedException); |
| } |
| } |
| |
| @Override |
| public SerializerResult entityCollection(final ServiceMetadata metadata, |
| final EdmEntityType entityType, final AbstractEntityCollection entitySet, |
| final EntityCollectionSerializerOptions options) throws SerializerException { |
| OutputStream outputStream = null; |
| SerializerException cachedException = null; |
| try { |
| CircleStreamBuffer buffer = new CircleStreamBuffer(); |
| outputStream = buffer.getOutputStream(); |
| JsonGenerator json = new JsonFactory().createGenerator(outputStream); |
| json.writeStartObject(); |
| |
| final ContextURL contextURL = checkContextURL(options == null ? null : options.getContextURL()); |
| writeContextURL(contextURL, json); |
| |
| writeMetadataETag(metadata, json); |
| |
| if (options != null && options.getCount() != null && options.getCount().getValue()) { |
| writeCount(entitySet, json); |
| } |
| writeOperations(entitySet.getOperations(), json); |
| json.writeFieldName(Constants.VALUE); |
| if (options == null) { |
| writeEntitySet(metadata, entityType, entitySet, null, null, false, json); |
| } else { |
| writeEntitySet(metadata, entityType, entitySet, |
| options.getExpand(), options.getSelect(), options.getWriteOnlyReferences(), json); |
| } |
| writeNextLink(entitySet, json); |
| |
| json.close(); |
| outputStream.close(); |
| return SerializerResultImpl.with().content(buffer.getInputStream()).build(); |
| } catch (final IOException e) { |
| cachedException = |
| new SerializerException(IO_EXCEPTION_TEXT, e, SerializerException.MessageKeys.IO_EXCEPTION); |
| throw cachedException; |
| } finally { |
| closeCircleStreamBufferOutput(outputStream, cachedException); |
| } |
| } |
| |
| @Override |
| public SerializerStreamResult entityCollectionStreamed(ServiceMetadata metadata, EdmEntityType entityType, |
| EntityIterator entities, EntityCollectionSerializerOptions options) throws SerializerException { |
| |
| return ODataWritableContent.with(entities, entityType, this, metadata, options).build(); |
| } |
| |
| public void entityCollectionIntoStream(final ServiceMetadata metadata, |
| final EdmEntityType entityType, final EntityIterator entitySet, |
| final EntityCollectionSerializerOptions options, final OutputStream outputStream) |
| throws SerializerException { |
| |
| SerializerException cachedException; |
| try { |
| JsonGenerator json = new JsonFactory().createGenerator(outputStream); |
| json.writeStartObject(); |
| |
| final ContextURL contextURL = checkContextURL(options == null ? null : options.getContextURL()); |
| writeContextURL(contextURL, json); |
| |
| writeMetadataETag(metadata, json); |
| |
| if (options != null && options.getCount() != null && options.getCount().getValue()) { |
| writeCount(entitySet, json); |
| } |
| json.writeFieldName(Constants.VALUE); |
| if (options == null) { |
| writeEntitySet(metadata, entityType, entitySet, null, null, false, json); |
| } else { |
| writeEntitySet(metadata, entityType, entitySet, |
| options.getExpand(), options.getSelect(), options.getWriteOnlyReferences(), json); |
| } |
| // next link not supported by default for streaming results |
| // writeNextLink(entitySet, json); |
| |
| json.close(); |
| } catch (final IOException e) { |
| cachedException = |
| new SerializerException(IO_EXCEPTION_TEXT, e, SerializerException.MessageKeys.IO_EXCEPTION); |
| throw cachedException; |
| } |
| } |
| |
| @Override |
| public SerializerResult entity(final ServiceMetadata metadata, final EdmEntityType entityType, |
| final Entity entity, final EntitySerializerOptions options) throws SerializerException { |
| OutputStream outputStream = null; |
| SerializerException cachedException = null; |
| try { |
| final ContextURL contextURL = checkContextURL(options == null ? null : options.getContextURL()); |
| CircleStreamBuffer buffer = new CircleStreamBuffer(); |
| outputStream = buffer.getOutputStream(); |
| JsonGenerator json = new JsonFactory().createGenerator(outputStream); |
| writeEntity(metadata, entityType, entity, contextURL, |
| options == null ? null : options.getExpand(), |
| options == null ? null : options.getSelect(), |
| options == null ? false : options.getWriteOnlyReferences(), |
| json); |
| |
| json.close(); |
| outputStream.close(); |
| return SerializerResultImpl.with().content(buffer.getInputStream()).build(); |
| } catch (final IOException e) { |
| cachedException = |
| new SerializerException(IO_EXCEPTION_TEXT, e, SerializerException.MessageKeys.IO_EXCEPTION); |
| throw cachedException; |
| } finally { |
| closeCircleStreamBufferOutput(outputStream, cachedException); |
| } |
| } |
| |
| ContextURL checkContextURL(final ContextURL contextURL) throws SerializerException { |
| if (isODataMetadataNone(contentType)) { |
| return null; |
| } else if (contextURL == null) { |
| throw new SerializerException("ContextURL null!", SerializerException.MessageKeys.NO_CONTEXT_URL); |
| } |
| return contextURL; |
| } |
| |
| protected void writeEntitySet(final ServiceMetadata metadata, final EdmEntityType entityType, |
| final AbstractEntityCollection entitySet, final ExpandOption expand, final SelectOption select, |
| final boolean onlyReference, final JsonGenerator json) throws IOException, |
| SerializerException { |
| json.writeStartArray(); |
| for (final Entity entity : entitySet) { |
| if (onlyReference) { |
| json.writeStartObject(); |
| json.writeStringField(Constants.JSON_ID, getEntityId(entity)); |
| json.writeEndObject(); |
| } else { |
| writeEntity(metadata, entityType, entity, null, expand, select, false, json); |
| } |
| } |
| json.writeEndArray(); |
| } |
| |
| /** |
| * Get the ascii representation of the entity id |
| * or thrown an {@link SerializerException} if id is <code>null</code>. |
| * |
| * @param entity the entity |
| * @return ascii representation of the entity id |
| */ |
| private String getEntityId(Entity entity) throws SerializerException { |
| if(entity.getId() == null) { |
| throw new SerializerException("Entity id is null.", SerializerException.MessageKeys.MISSING_ID); |
| } |
| return entity.getId().toASCIIString(); |
| } |
| |
| private boolean areKeyPredicateNamesSelected(SelectOption select, EdmEntityType type) { |
| if (select == null || ExpandSelectHelper.isAll(select)) { |
| return true; |
| } |
| final Set<String> selected = ExpandSelectHelper.getSelectedPropertyNames(select.getSelectItems()); |
| for (String key : type.getKeyPredicateNames()) { |
| if (!selected.contains(key)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| public void writeEntity(final ServiceMetadata metadata, final EdmEntityType entityType, final Entity entity, |
| final ContextURL contextURL, final ExpandOption expand, final SelectOption select, final boolean onlyReference, |
| final JsonGenerator json) |
| throws IOException, SerializerException { |
| json.writeStartObject(); |
| if (!isODataMetadataNone(contentType)) { |
| // top-level entity |
| if (contextURL != null) { |
| writeContextURL(contextURL, json); |
| writeMetadataETag(metadata, json); |
| } |
| if (entity.getETag() != null) { |
| json.writeStringField(Constants.JSON_ETAG, entity.getETag()); |
| } |
| if (entityType.hasStream()) { |
| if (entity.getMediaETag() != null) { |
| json.writeStringField(Constants.JSON_MEDIA_ETAG, entity.getMediaETag()); |
| } |
| if (entity.getMediaContentType() != null) { |
| json.writeStringField(Constants.JSON_MEDIA_CONTENT_TYPE, entity.getMediaContentType()); |
| } |
| if (entity.getMediaContentSource() != null) { |
| json.writeStringField(Constants.JSON_MEDIA_READ_LINK, entity.getMediaContentSource().toString()); |
| } |
| if (entity.getMediaEditLinks() != null && !entity.getMediaEditLinks().isEmpty()) { |
| json.writeStringField(Constants.JSON_MEDIA_EDIT_LINK, entity.getMediaEditLinks().get(0).getHref()); |
| } |
| } |
| } |
| if (onlyReference) { |
| json.writeStringField(Constants.JSON_ID, getEntityId(entity)); |
| } else { |
| final EdmEntityType resolvedType = resolveEntityType(metadata, entityType, entity.getType()); |
| if ((!isODataMetadataNone(contentType) && !resolvedType.equals(entityType)) |
| || isODataMetadataFull(contentType)) { |
| json.writeStringField(Constants.JSON_TYPE, "#" + entity.getType()); |
| } |
| if ((!isODataMetadataNone(contentType) && !areKeyPredicateNamesSelected(select, resolvedType)) |
| || isODataMetadataFull(contentType)) { |
| json.writeStringField(Constants.JSON_ID, getEntityId(entity)); |
| } |
| |
| if (isODataMetadataFull(contentType)) { |
| if (entity.getSelfLink() != null) { |
| json.writeStringField(Constants.JSON_READ_LINK, entity.getSelfLink().getHref()); |
| } |
| if (entity.getEditLink() != null) { |
| json.writeStringField(Constants.JSON_EDIT_LINK, entity.getEditLink().getHref()); |
| } |
| } |
| |
| writeProperties(metadata, resolvedType, entity.getProperties(), select, json); |
| writeNavigationProperties(metadata, resolvedType, entity, expand, json); |
| writeOperations(entity.getOperations(), json); |
| json.writeEndObject(); |
| } |
| } |
| |
| private void writeOperations(final List<Operation> operations, final JsonGenerator json) |
| throws IOException { |
| if (isODataMetadataFull(contentType)) { |
| for (Operation operation : operations) { |
| json.writeObjectFieldStart(operation.getMetadataAnchor()); |
| json.writeStringField(Constants.ATTR_TITLE, operation.getTitle()); |
| json.writeStringField(Constants.ATTR_TARGET, operation.getTarget().toASCIIString()); |
| json.writeEndObject(); |
| } |
| } |
| } |
| |
| protected EdmEntityType resolveEntityType(final ServiceMetadata metadata, final EdmEntityType baseType, |
| final String derivedTypeName) throws SerializerException { |
| if (derivedTypeName == null || |
| baseType.getFullQualifiedName().getFullQualifiedNameAsString().equals(derivedTypeName)) { |
| return baseType; |
| } |
| EdmEntityType derivedType = metadata.getEdm().getEntityType(new FullQualifiedName(derivedTypeName)); |
| if (derivedType == null) { |
| throw new SerializerException("EntityType not found", |
| SerializerException.MessageKeys.UNKNOWN_TYPE, derivedTypeName); |
| } |
| EdmEntityType type = derivedType.getBaseType(); |
| while (type != null) { |
| if (type.getFullQualifiedName().getFullQualifiedNameAsString() |
| .equals(baseType.getFullQualifiedName().getFullQualifiedNameAsString())) { |
| return derivedType; |
| } |
| type = type.getBaseType(); |
| } |
| throw new SerializerException("Wrong base type", |
| SerializerException.MessageKeys.WRONG_BASE_TYPE, derivedTypeName, baseType |
| .getFullQualifiedName().getFullQualifiedNameAsString()); |
| } |
| |
| protected EdmComplexType resolveComplexType(final ServiceMetadata metadata, final EdmComplexType baseType, |
| final String derivedTypeName) throws SerializerException { |
| if (derivedTypeName == null || |
| baseType.getFullQualifiedName().getFullQualifiedNameAsString().equals(derivedTypeName)) { |
| return baseType; |
| } |
| EdmComplexType derivedType = metadata.getEdm().getComplexType(new FullQualifiedName(derivedTypeName)); |
| if (derivedType == null) { |
| throw new SerializerException("Complex Type not found", |
| SerializerException.MessageKeys.UNKNOWN_TYPE, derivedTypeName); |
| } |
| EdmComplexType type = derivedType.getBaseType(); |
| while (type != null) { |
| if (type.getFullQualifiedName().getFullQualifiedNameAsString() |
| .equals(baseType.getFullQualifiedName().getFullQualifiedNameAsString())) { |
| return derivedType; |
| } |
| type = type.getBaseType(); |
| } |
| throw new SerializerException("Wrong base type", |
| SerializerException.MessageKeys.WRONG_BASE_TYPE, derivedTypeName, baseType |
| .getFullQualifiedName().getFullQualifiedNameAsString()); |
| } |
| |
| protected void writeProperties(final ServiceMetadata metadata, final EdmStructuredType type, |
| final List<Property> properties, |
| final SelectOption select, final JsonGenerator json) |
| throws IOException, SerializerException { |
| final boolean all = ExpandSelectHelper.isAll(select); |
| final Set<String> selected = all ? new HashSet<String>() : |
| ExpandSelectHelper.getSelectedPropertyNames(select.getSelectItems()); |
| for (final String propertyName : type.getPropertyNames()) { |
| if (all || selected.contains(propertyName)) { |
| final EdmProperty edmProperty = type.getStructuralProperty(propertyName); |
| final Property property = findProperty(propertyName, properties); |
| final Set<List<String>> selectedPaths = all || edmProperty.isPrimitive() ? null : |
| ExpandSelectHelper.getSelectedPaths(select.getSelectItems(), propertyName); |
| writeProperty(metadata, edmProperty, property, selectedPaths, json); |
| } |
| } |
| } |
| |
| protected void writeNavigationProperties(final ServiceMetadata metadata, |
| final EdmStructuredType type, final Linked linked, final ExpandOption expand, |
| final JsonGenerator json) throws SerializerException, IOException { |
| if (ExpandSelectHelper.hasExpand(expand)) { |
| final boolean expandAll = ExpandSelectHelper.isExpandAll(expand); |
| final Set<String> expanded = expandAll ? new HashSet<String>() : |
| ExpandSelectHelper.getExpandedPropertyNames(expand.getExpandItems()); |
| for (final String propertyName : type.getNavigationPropertyNames()) { |
| if (expandAll || expanded.contains(propertyName)) { |
| final EdmNavigationProperty property = type.getNavigationProperty(propertyName); |
| final Link navigationLink = linked.getNavigationLink(property.getName()); |
| final ExpandItem innerOptions = expandAll ? null : |
| ExpandSelectHelper.getExpandItem(expand.getExpandItems(), propertyName); |
| if (innerOptions != null && innerOptions.getLevelsOption() != null) { |
| throw new SerializerException("Expand option $levels is not supported.", |
| SerializerException.MessageKeys.NOT_IMPLEMENTED); |
| } |
| writeExpandedNavigationProperty(metadata, property, navigationLink, |
| innerOptions == null ? null : innerOptions.getExpandOption(), |
| innerOptions == null ? null : innerOptions.getSelectOption(), |
| innerOptions == null ? null : innerOptions.getCountOption(), |
| innerOptions == null ? false : innerOptions.hasCountPath(), |
| innerOptions == null ? false : innerOptions.isRef(), |
| json); |
| } |
| } |
| } else if (isODataMetadataFull(contentType)) { |
| for (final String propertyName : type.getNavigationPropertyNames()) { |
| final Link navigationLink = linked.getNavigationLink(propertyName); |
| if (navigationLink != null) { |
| json.writeStringField(propertyName + Constants.JSON_NAVIGATION_LINK, navigationLink.getHref()); |
| } |
| final Link associationLink = linked.getAssociationLink(propertyName); |
| if (associationLink != null) { |
| json.writeStringField(propertyName + Constants.JSON_ASSOCIATION_LINK, associationLink.getHref()); |
| } |
| } |
| } |
| } |
| |
| protected void writeExpandedNavigationProperty( |
| final ServiceMetadata metadata, final EdmNavigationProperty property, |
| final Link navigationLink, final ExpandOption innerExpand, |
| final SelectOption innerSelect, final CountOption innerCount, |
| final boolean writeOnlyCount, final boolean writeOnlyRef, |
| final JsonGenerator json) throws IOException, SerializerException { |
| |
| if (property.isCollection()) { |
| if (writeOnlyCount) { |
| if (navigationLink == null || navigationLink.getInlineEntitySet() == null) { |
| writeInlineCount(property.getName(), 0, json); |
| } else { |
| writeInlineCount(property.getName(), navigationLink.getInlineEntitySet().getCount(), json); |
| } |
| } else { |
| if (navigationLink == null || navigationLink.getInlineEntitySet() == null) { |
| if (innerCount != null && innerCount.getValue()) { |
| writeInlineCount(property.getName(), 0, json); |
| } |
| json.writeFieldName(property.getName()); |
| json.writeStartArray(); |
| json.writeEndArray(); |
| } else { |
| if (innerCount != null && innerCount.getValue()) { |
| writeInlineCount(property.getName(), navigationLink.getInlineEntitySet().getCount(), json); |
| } |
| json.writeFieldName(property.getName()); |
| writeEntitySet(metadata, property.getType(), navigationLink.getInlineEntitySet(), innerExpand, |
| innerSelect, writeOnlyRef, json); |
| } |
| } |
| } else { |
| json.writeFieldName(property.getName()); |
| if (navigationLink == null || navigationLink.getInlineEntity() == null) { |
| json.writeNull(); |
| } else { |
| writeEntity(metadata, property.getType(), navigationLink.getInlineEntity(), null, |
| innerExpand, innerSelect, writeOnlyRef, json); |
| } |
| } |
| } |
| |
| private boolean isStreamProperty(EdmProperty edmProperty) { |
| final EdmType type = edmProperty.getType(); |
| return (edmProperty.isPrimitive() && type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Stream)); |
| } |
| |
| protected void writeProperty(final ServiceMetadata metadata, |
| final EdmProperty edmProperty, final Property property, |
| final Set<List<String>> selectedPaths, final JsonGenerator json) |
| throws IOException, SerializerException { |
| boolean isStreamProperty = isStreamProperty(edmProperty); |
| writePropertyType(edmProperty, property, json); |
| if (!isStreamProperty) { |
| json.writeFieldName(edmProperty.getName()); |
| } |
| if ((property == null || property.isNull())) { |
| if (edmProperty.isNullable() == Boolean.FALSE) { |
| throw new SerializerException("Non-nullable property not present!", |
| SerializerException.MessageKeys.MISSING_PROPERTY, edmProperty.getName()); |
| } else { |
| if (!isStreamProperty) { |
| if (edmProperty.isCollection()) { |
| json.writeStartArray(); |
| json.writeEndArray(); |
| } else { |
| json.writeNull(); |
| } |
| } |
| } |
| } else { |
| writePropertyValue(metadata, edmProperty, property, selectedPaths, json); |
| } |
| } |
| |
| private void writePropertyType(final EdmProperty edmProperty, final Property property, |
| final JsonGenerator json) throws SerializerException, IOException { |
| if(!isODataMetadataFull(contentType)) { |
| return; |
| } |
| String typeName = edmProperty.getName()+Constants.JSON_TYPE; |
| final EdmType type = edmProperty.getType(); |
| if (type.getKind() == EdmTypeKind.ENUM || type.getKind() == EdmTypeKind.DEFINITION) { |
| if (edmProperty.isCollection()) { |
| json.writeStringField(typeName, |
| "#Collection("+type.getFullQualifiedName().getFullQualifiedNameAsString()+")"); |
| } else { |
| json.writeStringField(typeName, "#"+type.getFullQualifiedName().getFullQualifiedNameAsString()); |
| } |
| } else if (edmProperty.isPrimitive()) { |
| if (edmProperty.isCollection()) { |
| json.writeStringField(typeName, |
| "#Collection("+type.getFullQualifiedName().getName()+")"); |
| } else { |
| // exclude the properties that can be heuristically determined |
| if (type != EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Boolean) && |
| type != EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Double) && |
| type != EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.String)) { |
| json.writeStringField(typeName, |
| "#"+type.getFullQualifiedName().getName()); |
| } |
| } |
| } else if (type.getKind() == EdmTypeKind.COMPLEX) { |
| // non-collection case written in writeComplex method directly. |
| if (edmProperty.isCollection()) { |
| json.writeStringField(typeName, |
| "#Collection("+type.getFullQualifiedName().getFullQualifiedNameAsString()+")"); |
| } |
| } else { |
| throw new SerializerException("Property type not yet supported!", |
| SerializerException.MessageKeys.UNSUPPORTED_PROPERTY_TYPE, edmProperty.getName()); |
| } |
| } |
| |
| private void writePropertyValue(final ServiceMetadata metadata, final EdmProperty edmProperty, |
| final Property property, final Set<List<String>> selectedPaths, final JsonGenerator json) |
| throws IOException, SerializerException { |
| final EdmType type = edmProperty.getType(); |
| try { |
| if (edmProperty.isPrimitive() |
| || type.getKind() == EdmTypeKind.ENUM || type.getKind() == EdmTypeKind.DEFINITION) { |
| if (edmProperty.isCollection()) { |
| writePrimitiveCollection((EdmPrimitiveType) type, property, |
| edmProperty.isNullable(), edmProperty.getMaxLength(), |
| edmProperty.getPrecision(), edmProperty.getScale(), edmProperty.isUnicode(), json); |
| } else { |
| writePrimitive((EdmPrimitiveType) type, property, |
| edmProperty.isNullable(), edmProperty.getMaxLength(), |
| edmProperty.getPrecision(), edmProperty.getScale(), edmProperty.isUnicode(), json); |
| } |
| } else if (property.isComplex()) { |
| if (edmProperty.isCollection()) { |
| writeComplexCollection(metadata, (EdmComplexType) type, property, selectedPaths, json); |
| } else { |
| writeComplexValue(metadata, property, (EdmComplexType) type, property.asComplex().getValue(), selectedPaths, |
| json); |
| } |
| } else { |
| throw new SerializerException("Property type not yet supported!", |
| SerializerException.MessageKeys.UNSUPPORTED_PROPERTY_TYPE, edmProperty.getName()); |
| } |
| } catch (final EdmPrimitiveTypeException e) { |
| throw new SerializerException("Wrong value for property!", e, |
| SerializerException.MessageKeys.WRONG_PROPERTY_VALUE, |
| edmProperty.getName(), property.getValue().toString()); |
| } |
| } |
| |
| private void writePrimitiveCollection(final EdmPrimitiveType type, final Property property, |
| final Boolean isNullable, final Integer maxLength, final Integer precision, final Integer scale, |
| final Boolean isUnicode, final JsonGenerator json) |
| throws IOException, SerializerException { |
| json.writeStartArray(); |
| for (Object value : property.asCollection()) { |
| switch (property.getValueType()) { |
| case COLLECTION_PRIMITIVE: |
| case COLLECTION_ENUM: |
| try { |
| writePrimitiveValue(property.getName(), type, value, isNullable, |
| maxLength, precision, scale, isUnicode, json); |
| } catch (EdmPrimitiveTypeException e) { |
| throw new SerializerException("Wrong value for property!", e, |
| SerializerException.MessageKeys.WRONG_PROPERTY_VALUE, |
| property.getName(), property.getValue().toString()); |
| } |
| break; |
| case COLLECTION_GEOSPATIAL: |
| throw new SerializerException("Property type not yet supported!", |
| SerializerException.MessageKeys.UNSUPPORTED_PROPERTY_TYPE, property.getName()); |
| default: |
| throw new SerializerException("Property type not yet supported!", |
| SerializerException.MessageKeys.UNSUPPORTED_PROPERTY_TYPE, property.getName()); |
| } |
| } |
| json.writeEndArray(); |
| } |
| |
| private void writeComplexCollection(final ServiceMetadata metadata, final EdmComplexType type, |
| final Property property, |
| final Set<List<String>> selectedPaths, final JsonGenerator json) |
| throws IOException, SerializerException { |
| json.writeStartArray(); |
| for (Object value : property.asCollection()) { |
| switch (property.getValueType()) { |
| case COLLECTION_COMPLEX: |
| writeComplexValue(metadata, property, type, ((ComplexValue) value).getValue(), selectedPaths, json); |
| break; |
| default: |
| throw new SerializerException("Property type not yet supported!", |
| SerializerException.MessageKeys.UNSUPPORTED_PROPERTY_TYPE, property.getName()); |
| } |
| } |
| json.writeEndArray(); |
| } |
| |
| private void writePrimitive(final EdmPrimitiveType type, final Property property, |
| final Boolean isNullable, final Integer maxLength, final Integer precision, final Integer scale, |
| final Boolean isUnicode, final JsonGenerator json) |
| throws EdmPrimitiveTypeException, IOException, SerializerException { |
| if (property.isPrimitive()) { |
| writePrimitiveValue(property.getName(), type, property.asPrimitive(), |
| isNullable, maxLength, precision, scale, isUnicode, json); |
| } else if (property.isGeospatial()) { |
| throw new SerializerException("Property type not yet supported!", |
| SerializerException.MessageKeys.UNSUPPORTED_PROPERTY_TYPE, property.getName()); |
| } else if (property.isEnum()) { |
| writePrimitiveValue(property.getName(), type, property.asEnum(), |
| isNullable, maxLength, precision, scale, isUnicode, json); |
| } else { |
| throw new SerializerException("Inconsistent property type!", |
| SerializerException.MessageKeys.INCONSISTENT_PROPERTY_TYPE, property.getName()); |
| } |
| } |
| |
| protected void writePrimitiveValue(final String name, final EdmPrimitiveType type, final Object primitiveValue, |
| final Boolean isNullable, final Integer maxLength, final Integer precision, final Integer scale, |
| final Boolean isUnicode, final JsonGenerator json) throws EdmPrimitiveTypeException, IOException { |
| final String value = type.valueToString(primitiveValue, |
| isNullable, maxLength, precision, scale, isUnicode); |
| if (value == null) { |
| json.writeNull(); |
| } else if (type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Boolean)) { |
| json.writeBoolean(Boolean.parseBoolean(value)); |
| } else if (type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Byte) |
| || type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Double) |
| || type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Int16) |
| || type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Int32) |
| || type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.SByte) |
| || type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Single) |
| || (type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Decimal) |
| || type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Int64)) |
| && !isIEEE754Compatible) { |
| json.writeNumber(value); |
| } else if (type == EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Stream)) { |
| if (primitiveValue instanceof Link) { |
| Link stream = (Link)primitiveValue; |
| if (!isODataMetadataNone(contentType)) { |
| if (stream.getMediaETag() != null) { |
| json.writeStringField(name+Constants.JSON_MEDIA_ETAG, stream.getMediaETag()); |
| } |
| if (stream.getType() != null) { |
| json.writeStringField(name+Constants.JSON_MEDIA_CONTENT_TYPE, stream.getType()); |
| } |
| } |
| if (isODataMetadataFull(contentType)) { |
| if (stream.getRel() != null && stream.getRel().equals(Constants.NS_MEDIA_READ_LINK_REL)) { |
| json.writeStringField(name+Constants.JSON_MEDIA_READ_LINK, stream.getHref()); |
| } |
| if (stream.getRel() == null || stream.getRel().equals(Constants.NS_MEDIA_EDIT_LINK_REL)) { |
| json.writeStringField(name+Constants.JSON_MEDIA_EDIT_LINK, stream.getHref()); |
| } |
| } |
| } |
| } else { |
| json.writeString(value); |
| } |
| } |
| |
| protected void writeComplexValue(final ServiceMetadata metadata, final Property complexProperty, |
| final EdmComplexType type, final List<Property> properties, |
| final Set<List<String>> selectedPaths, final JsonGenerator json) |
| throws IOException, SerializerException { |
| json.writeStartObject(); |
| |
| final EdmComplexType resolvedType = resolveComplexType(metadata, |
| type, complexProperty.getType()); |
| if (!isODataMetadataNone(contentType) && !resolvedType.equals(type) || isODataMetadataFull(contentType)) { |
| json.writeStringField(Constants.JSON_TYPE, "#" + complexProperty.getType()); |
| } |
| |
| for (final String propertyName : resolvedType.getPropertyNames()) { |
| final Property property = findProperty(propertyName, properties); |
| if (selectedPaths == null || ExpandSelectHelper.isSelected(selectedPaths, propertyName)) { |
| writeProperty(metadata, (EdmProperty) resolvedType.getProperty(propertyName), property, |
| selectedPaths == null ? null : ExpandSelectHelper.getReducedSelectedPaths(selectedPaths, propertyName), |
| json); |
| } |
| } |
| json.writeEndObject(); |
| } |
| |
| private Property findProperty(final String propertyName, final List<Property> properties) { |
| for (final Property property : properties) { |
| if (propertyName.equals(property.getName())) { |
| return property; |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| public SerializerResult primitive(final ServiceMetadata metadata, final EdmPrimitiveType type, |
| final Property property, final PrimitiveSerializerOptions options) throws SerializerException { |
| OutputStream outputStream = null; |
| SerializerException cachedException = null; |
| try { |
| final ContextURL contextURL = checkContextURL(options == null ? null : options.getContextURL()); |
| CircleStreamBuffer buffer = new CircleStreamBuffer(); |
| outputStream = buffer.getOutputStream(); |
| JsonGenerator json = new JsonFactory().createGenerator(outputStream); |
| json.writeStartObject(); |
| writeContextURL(contextURL, json); |
| writeMetadataETag(metadata, json); |
| writeOperations(property.getOperations(), json); |
| if (property.isNull()) { |
| throw new SerializerException("Property value can not be null.", SerializerException.MessageKeys.NULL_INPUT); |
| } else { |
| json.writeFieldName(Constants.VALUE); |
| writePrimitive(type, property, |
| options == null ? null : options.isNullable(), |
| options == null ? null : options.getMaxLength(), |
| options == null ? null : options.getPrecision(), |
| options == null ? null : options.getScale(), |
| options == null ? null : options.isUnicode(), json); |
| } |
| json.writeEndObject(); |
| |
| json.close(); |
| outputStream.close(); |
| return SerializerResultImpl.with().content(buffer.getInputStream()).build(); |
| } catch (final IOException e) { |
| cachedException = |
| new SerializerException(IO_EXCEPTION_TEXT, e, SerializerException.MessageKeys.IO_EXCEPTION); |
| throw cachedException; |
| } catch (final EdmPrimitiveTypeException e) { |
| cachedException = new SerializerException("Wrong value for property!", e, |
| SerializerException.MessageKeys.WRONG_PROPERTY_VALUE, |
| property.getName(), property.getValue().toString()); |
| throw cachedException; |
| } finally { |
| closeCircleStreamBufferOutput(outputStream, cachedException); |
| } |
| } |
| |
| @Override |
| public SerializerResult complex(final ServiceMetadata metadata, final EdmComplexType type, |
| final Property property, final ComplexSerializerOptions options) throws SerializerException { |
| OutputStream outputStream = null; |
| SerializerException cachedException = null; |
| try { |
| final ContextURL contextURL = checkContextURL(options == null ? null : options.getContextURL()); |
| CircleStreamBuffer buffer = new CircleStreamBuffer(); |
| outputStream = buffer.getOutputStream(); |
| JsonGenerator json = new JsonFactory().createGenerator(outputStream); |
| json.writeStartObject(); |
| writeContextURL(contextURL, json); |
| writeMetadataETag(metadata, json); |
| final EdmComplexType resolvedType = resolveComplexType(metadata, type, property.getType()); |
| if (!isODataMetadataNone(contentType) && !resolvedType.equals(type) || isODataMetadataFull(contentType)) { |
| json.writeStringField(Constants.JSON_TYPE, "#" + property.getType()); |
| } |
| writeOperations(property.getOperations(), json); |
| final List<Property> values = |
| property.isNull() ? Collections.<Property> emptyList() : property.asComplex().getValue(); |
| writeProperties(metadata, type, values, options == null ? null : options.getSelect(), json); |
| if (!property.isNull() && property.isComplex()) { |
| writeNavigationProperties(metadata, type, property.asComplex(), |
| options == null ? null : options.getExpand(), json); |
| } |
| json.writeEndObject(); |
| |
| json.close(); |
| outputStream.close(); |
| return SerializerResultImpl.with().content(buffer.getInputStream()).build(); |
| } catch (final IOException e) { |
| cachedException = |
| new SerializerException(IO_EXCEPTION_TEXT, e, SerializerException.MessageKeys.IO_EXCEPTION); |
| throw cachedException; |
| } finally { |
| closeCircleStreamBufferOutput(outputStream, cachedException); |
| } |
| } |
| |
| @Override |
| public SerializerResult primitiveCollection(final ServiceMetadata metadata, final EdmPrimitiveType type, |
| final Property property, final PrimitiveSerializerOptions options) throws SerializerException { |
| OutputStream outputStream = null; |
| SerializerException cachedException = null; |
| try { |
| final ContextURL contextURL = checkContextURL(options == null ? null : options.getContextURL()); |
| CircleStreamBuffer buffer = new CircleStreamBuffer(); |
| outputStream = buffer.getOutputStream(); |
| JsonGenerator json = new JsonFactory().createGenerator(outputStream); |
| json.writeStartObject(); |
| writeContextURL(contextURL, json); |
| writeMetadataETag(metadata, json); |
| if (isODataMetadataFull(contentType)) { |
| json.writeStringField(Constants.JSON_TYPE, "#Collection("+type.getFullQualifiedName().getName()+")"); |
| } |
| writeOperations(property.getOperations(), json); |
| json.writeFieldName(Constants.VALUE); |
| writePrimitiveCollection(type, property, |
| options == null ? null : options.isNullable(), |
| options == null ? null : options.getMaxLength(), |
| options == null ? null : options.getPrecision(), |
| options == null ? null : options.getScale(), |
| options == null ? null : options.isUnicode(), json); |
| json.writeEndObject(); |
| |
| json.close(); |
| outputStream.close(); |
| return SerializerResultImpl.with().content(buffer.getInputStream()).build(); |
| } catch (final IOException e) { |
| cachedException = |
| new SerializerException(IO_EXCEPTION_TEXT, e, SerializerException.MessageKeys.IO_EXCEPTION); |
| throw cachedException; |
| } finally { |
| closeCircleStreamBufferOutput(outputStream, cachedException); |
| } |
| } |
| |
| @Override |
| public SerializerResult complexCollection(final ServiceMetadata metadata, final EdmComplexType type, |
| final Property property, final ComplexSerializerOptions options) throws SerializerException { |
| OutputStream outputStream = null; |
| SerializerException cachedException = null; |
| try { |
| final ContextURL contextURL = checkContextURL(options == null ? null : options.getContextURL()); |
| CircleStreamBuffer buffer = new CircleStreamBuffer(); |
| outputStream = buffer.getOutputStream(); |
| JsonGenerator json = new JsonFactory().createGenerator(outputStream); |
| json.writeStartObject(); |
| writeContextURL(contextURL, json); |
| writeMetadataETag(metadata, json); |
| if (isODataMetadataFull(contentType)) { |
| json.writeStringField(Constants.JSON_TYPE, |
| "#Collection("+type.getFullQualifiedName().getFullQualifiedNameAsString()+")"); |
| } |
| writeOperations(property.getOperations(), json); |
| json.writeFieldName(Constants.VALUE); |
| writeComplexCollection(metadata, type, property, null, json); |
| json.writeEndObject(); |
| |
| json.close(); |
| outputStream.close(); |
| return SerializerResultImpl.with().content(buffer.getInputStream()).build(); |
| } catch (final IOException e) { |
| cachedException = |
| new SerializerException(IO_EXCEPTION_TEXT, e, SerializerException.MessageKeys.IO_EXCEPTION); |
| throw cachedException; |
| } finally { |
| closeCircleStreamBufferOutput(outputStream, cachedException); |
| } |
| } |
| |
| @Override |
| public SerializerResult reference(final ServiceMetadata metadata, final EdmEntitySet edmEntitySet, |
| final Entity entity, final ReferenceSerializerOptions options) throws SerializerException { |
| OutputStream outputStream = null; |
| SerializerException cachedException = null; |
| |
| try { |
| final ContextURL contextURL = checkContextURL(options == null ? null : options.getContextURL()); |
| CircleStreamBuffer buffer = new CircleStreamBuffer(); |
| final UriHelper uriHelper = new UriHelperImpl(); |
| outputStream = buffer.getOutputStream(); |
| final JsonGenerator json = new JsonFactory().createGenerator(outputStream); |
| |
| json.writeStartObject(); |
| writeContextURL(contextURL, json); |
| json.writeStringField(Constants.JSON_ID, uriHelper.buildCanonicalURL(edmEntitySet, entity)); |
| json.writeEndObject(); |
| |
| json.close(); |
| outputStream.close(); |
| return SerializerResultImpl.with().content(buffer.getInputStream()).build(); |
| } catch (final IOException e) { |
| cachedException = |
| new SerializerException(IO_EXCEPTION_TEXT, e, SerializerException.MessageKeys.IO_EXCEPTION); |
| throw cachedException; |
| } finally { |
| closeCircleStreamBufferOutput(outputStream, cachedException); |
| } |
| } |
| |
| @Override |
| public SerializerResult referenceCollection(final ServiceMetadata metadata, final EdmEntitySet edmEntitySet, |
| final AbstractEntityCollection entityCollection, final ReferenceCollectionSerializerOptions options) |
| throws SerializerException { |
| OutputStream outputStream = null; |
| SerializerException cachedException = null; |
| |
| try { |
| final ContextURL contextURL = checkContextURL(options == null ? null : options.getContextURL()); |
| CircleStreamBuffer buffer = new CircleStreamBuffer(); |
| final UriHelper uriHelper = new UriHelperImpl(); |
| outputStream = buffer.getOutputStream(); |
| final JsonGenerator json = new JsonFactory().createGenerator(outputStream); |
| json.writeStartObject(); |
| |
| writeContextURL(contextURL, json); |
| if (options != null && options.getCount() != null && options.getCount().getValue()) { |
| writeCount(entityCollection, json); |
| } |
| |
| json.writeArrayFieldStart(Constants.VALUE); |
| for (final Entity entity : entityCollection) { |
| json.writeStartObject(); |
| json.writeStringField(Constants.JSON_ID, uriHelper.buildCanonicalURL(edmEntitySet, entity)); |
| json.writeEndObject(); |
| } |
| json.writeEndArray(); |
| |
| writeNextLink(entityCollection, json); |
| |
| json.writeEndObject(); |
| |
| json.close(); |
| outputStream.close(); |
| return SerializerResultImpl.with().content(buffer.getInputStream()).build(); |
| } catch (final IOException e) { |
| cachedException = |
| new SerializerException(IO_EXCEPTION_TEXT, e, SerializerException.MessageKeys.IO_EXCEPTION); |
| throw cachedException; |
| } finally { |
| closeCircleStreamBufferOutput(outputStream, cachedException); |
| } |
| |
| } |
| |
| void writeContextURL(final ContextURL contextURL, final JsonGenerator json) throws IOException { |
| if (!isODataMetadataNone(contentType) && contextURL != null) { |
| json.writeStringField(Constants.JSON_CONTEXT, ContextURLBuilder.create(contextURL).toASCIIString()); |
| } |
| } |
| |
| void writeMetadataETag(final ServiceMetadata metadata, final JsonGenerator json) throws IOException { |
| if (!isODataMetadataNone(contentType) |
| && metadata != null |
| && metadata.getServiceMetadataETagSupport() != null |
| && metadata.getServiceMetadataETagSupport().getMetadataETag() != null) { |
| json.writeStringField(Constants.JSON_METADATA_ETAG, |
| metadata.getServiceMetadataETagSupport().getMetadataETag()); |
| } |
| } |
| |
| void writeCount(final AbstractEntityCollection entityCollection, final JsonGenerator json) throws IOException { |
| if (entityCollection.getCount() != null) { |
| if (isIEEE754Compatible) { |
| json.writeStringField(Constants.JSON_COUNT, entityCollection.getCount().toString()); |
| } else { |
| json.writeNumberField(Constants.JSON_COUNT, entityCollection.getCount()); |
| } |
| } |
| } |
| |
| void writeInlineCount(final String propertyName, |
| final Integer count, final JsonGenerator json) |
| throws IOException { |
| if (count != null) { |
| if (isIEEE754Compatible) { |
| json.writeStringField(propertyName + Constants.JSON_COUNT, String.valueOf(count)); |
| } else { |
| json.writeNumberField(propertyName + Constants.JSON_COUNT, count); |
| } |
| } |
| } |
| |
| void writeNextLink(final AbstractEntityCollection entitySet, final JsonGenerator json) throws IOException { |
| if (entitySet.getNext() != null) { |
| json.writeStringField(Constants.JSON_NEXT_LINK, entitySet.getNext().toASCIIString()); |
| } |
| } |
| |
| private boolean isODataIEEE754Compatible(final ContentType contentType) { |
| return contentType.getParameters().containsKey(ContentType.PARAMETER_IEEE754_COMPATIBLE) |
| && Boolean.TRUE.toString().equalsIgnoreCase( |
| contentType.getParameter(ContentType.PARAMETER_IEEE754_COMPATIBLE).toLowerCase()); |
| } |
| } |