[OLINGO-1226] Serializer to construct metadata document in json format

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

Branch: refs/heads/master
Commit: d55ed59d0d8a6f28328aac951706391070ba269b
Parents: 1012bcb
Author: ramya vasanth <[email protected]>
Authored: Wed Jan 31 10:50:48 2018 +0530
Committer: ramya vasanth <[email protected]>
Committed: Wed Jan 31 10:50:48 2018 +0530

----------------------------------------------------------------------
 .../olingo/server/api/serializer/Kind.java      |   52 +
 .../olingo/server/core/ContentNegotiator.java   |   22 +-
 .../json/MetadataDocumentJsonSerializer.java    | 1173 +++++++++++++++++
 .../serializer/json/ODataJsonSerializer.java    |   21 +-
 .../server/core/ContentNegotiatorTest.java      |   43 +-
 .../MetadataDocumentJsonSerializerTest.java     | 1189 ++++++++++++++++++
 6 files changed, 2486 insertions(+), 14 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/d55ed59d/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/Kind.java
----------------------------------------------------------------------
diff --git 
a/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/Kind.java
 
b/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/Kind.java
new file mode 100644
index 0000000..2450db9
--- /dev/null
+++ 
b/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/Kind.java
@@ -0,0 +1,52 @@
+/*
+ * 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.api.serializer;
+
+/**
+ * The different types of edm kinds that form the metadata
+ * 
+ */
+public enum Kind {
+  /** EntityType */
+  EntityType,
+  /** ComplexType */
+  ComplexType,
+  /** Function Import */
+  FunctionImport,
+  /** Action Import */
+  ActionImport,
+  /** Term */
+  Term,
+  /** Navigation Property */
+  NavigationProperty,
+  /** Enum Type **/
+  EnumType,
+  /** Singleton **/
+  Singleton,
+  /** Extending **/
+  Extending,
+  /** Entity Container **/
+  EntityContainer,
+  /** Entity Set **/
+  EntitySet,
+  /** Function **/
+  Function,
+  /** Action **/
+  Action
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/d55ed59d/lib/server-core/src/main/java/org/apache/olingo/server/core/ContentNegotiator.java
----------------------------------------------------------------------
diff --git 
a/lib/server-core/src/main/java/org/apache/olingo/server/core/ContentNegotiator.java
 
b/lib/server-core/src/main/java/org/apache/olingo/server/core/ContentNegotiator.java
index 63452f4..4aaeee7 100644
--- 
a/lib/server-core/src/main/java/org/apache/olingo/server/core/ContentNegotiator.java
+++ 
b/lib/server-core/src/main/java/org/apache/olingo/server/core/ContentNegotiator.java
@@ -34,7 +34,9 @@ public final class ContentNegotiator {
 
   private static final String ATOM = "atom";
   private static final String JSON = "json";
+  private static final String APPLICATION_JSON = "application/json";
   private static final String XML = "xml";
+  private static final String METADATA = "METADATA";
 
   private static final List<ContentType> DEFAULT_SUPPORTED_CONTENT_TYPES =
       Collections.unmodifiableList(Arrays.asList(
@@ -50,7 +52,8 @@ public final class ContentNegotiator {
   private static List<ContentType> getDefaultSupportedContentTypes(final 
RepresentationType type) {
     switch (type) {
     case METADATA:
-      return Collections.singletonList(ContentType.APPLICATION_XML);
+      return 
Collections.unmodifiableList(Arrays.asList(ContentType.APPLICATION_XML,
+          ContentType.APPLICATION_JSON));
     case MEDIA:
     case BINARY:
       return Collections.singletonList(ContentType.APPLICATION_OCTET_STREAM);
@@ -88,7 +91,7 @@ public final class ContentNegotiator {
 
     if (formatOption != null && formatOption.getFormat() != null) {
       final String formatString = formatOption.getFormat().trim();
-      final ContentType contentType = mapContentType(formatString);
+      final ContentType contentType = mapContentType(formatString, 
representationType);
 
       try {
         result = getAcceptedType(
@@ -126,10 +129,19 @@ public final class ContentNegotiator {
     return result;
   }
 
-  private static ContentType mapContentType(final String formatString) {
-    return JSON.equalsIgnoreCase(formatString) ? ContentType.JSON :
+  private static ContentType mapContentType(final String formatString, 
+      RepresentationType representationType) {
+    if (representationType.name().equals(METADATA)) {
+      return JSON.equalsIgnoreCase(formatString) ||
+          APPLICATION_JSON.equalsIgnoreCase(formatString) ? 
ContentType.APPLICATION_JSON :
         XML.equalsIgnoreCase(formatString) ? ContentType.APPLICATION_XML :
-            ATOM.equalsIgnoreCase(formatString) ? 
ContentType.APPLICATION_ATOM_XML : null;
+          ATOM.equalsIgnoreCase(formatString) ? 
ContentType.APPLICATION_ATOM_XML : null;
+    } else {
+      return JSON.equalsIgnoreCase(formatString) ? ContentType.JSON :
+          XML.equalsIgnoreCase(formatString) ? ContentType.APPLICATION_XML :
+              ATOM.equalsIgnoreCase(formatString) ? 
ContentType.APPLICATION_ATOM_XML : 
+                APPLICATION_JSON.equalsIgnoreCase(formatString)? 
ContentType.APPLICATION_JSON: null;
+    }
   }
 
   private static ContentType getAcceptedType(final List<AcceptType> 
acceptedContentTypes,

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/d55ed59d/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/MetadataDocumentJsonSerializer.java
----------------------------------------------------------------------
diff --git 
a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/MetadataDocumentJsonSerializer.java
 
b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/MetadataDocumentJsonSerializer.java
new file mode 100644
index 0000000..7c9e957
--- /dev/null
+++ 
b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/MetadataDocumentJsonSerializer.java
@@ -0,0 +1,1173 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.olingo.server.core.serializer.json;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.olingo.commons.api.edm.EdmAction;
+import org.apache.olingo.commons.api.edm.EdmActionImport;
+import org.apache.olingo.commons.api.edm.EdmAnnotatable;
+import org.apache.olingo.commons.api.edm.EdmAnnotation;
+import org.apache.olingo.commons.api.edm.EdmAnnotations;
+import org.apache.olingo.commons.api.edm.EdmBindingTarget;
+import org.apache.olingo.commons.api.edm.EdmComplexType;
+import org.apache.olingo.commons.api.edm.EdmEntityContainer;
+import org.apache.olingo.commons.api.edm.EdmEntitySet;
+import org.apache.olingo.commons.api.edm.EdmEntityType;
+import org.apache.olingo.commons.api.edm.EdmEnumType;
+import org.apache.olingo.commons.api.edm.EdmFunction;
+import org.apache.olingo.commons.api.edm.EdmFunctionImport;
+import org.apache.olingo.commons.api.edm.EdmKeyPropertyRef;
+import org.apache.olingo.commons.api.edm.EdmMember;
+import org.apache.olingo.commons.api.edm.EdmNavigationProperty;
+import org.apache.olingo.commons.api.edm.EdmNavigationPropertyBinding;
+import org.apache.olingo.commons.api.edm.EdmOperation;
+import org.apache.olingo.commons.api.edm.EdmParameter;
+import org.apache.olingo.commons.api.edm.EdmProperty;
+import org.apache.olingo.commons.api.edm.EdmReferentialConstraint;
+import org.apache.olingo.commons.api.edm.EdmReturnType;
+import org.apache.olingo.commons.api.edm.EdmSchema;
+import org.apache.olingo.commons.api.edm.EdmSingleton;
+import org.apache.olingo.commons.api.edm.EdmStructuredType;
+import org.apache.olingo.commons.api.edm.EdmTerm;
+import org.apache.olingo.commons.api.edm.EdmType;
+import org.apache.olingo.commons.api.edm.EdmTypeDefinition;
+import org.apache.olingo.commons.api.edm.FullQualifiedName;
+import org.apache.olingo.commons.api.edm.TargetType;
+import org.apache.olingo.commons.api.edm.annotation.EdmApply;
+import org.apache.olingo.commons.api.edm.annotation.EdmCast;
+import org.apache.olingo.commons.api.edm.annotation.EdmConstantExpression;
+import org.apache.olingo.commons.api.edm.annotation.EdmDynamicExpression;
+import org.apache.olingo.commons.api.edm.annotation.EdmExpression;
+import org.apache.olingo.commons.api.edm.annotation.EdmIf;
+import org.apache.olingo.commons.api.edm.annotation.EdmIsOf;
+import org.apache.olingo.commons.api.edm.annotation.EdmLabeledElement;
+import org.apache.olingo.commons.api.edm.annotation.EdmLabeledElementReference;
+import 
org.apache.olingo.commons.api.edm.annotation.EdmLogicalOrComparisonExpression;
+import org.apache.olingo.commons.api.edm.annotation.EdmNavigationPropertyPath;
+import org.apache.olingo.commons.api.edm.annotation.EdmNot;
+import org.apache.olingo.commons.api.edm.annotation.EdmNull;
+import org.apache.olingo.commons.api.edm.annotation.EdmPath;
+import org.apache.olingo.commons.api.edm.annotation.EdmPropertyPath;
+import org.apache.olingo.commons.api.edm.annotation.EdmPropertyValue;
+import org.apache.olingo.commons.api.edm.annotation.EdmRecord;
+import org.apache.olingo.commons.api.edm.annotation.EdmUrlRef;
+import org.apache.olingo.commons.api.edm.constants.EdmTypeKind;
+import org.apache.olingo.commons.api.edmx.EdmxReference;
+import org.apache.olingo.commons.api.edmx.EdmxReferenceInclude;
+import org.apache.olingo.commons.api.edmx.EdmxReferenceIncludeAnnotation;
+import org.apache.olingo.server.api.ServiceMetadata;
+import org.apache.olingo.server.api.serializer.Kind;
+import org.apache.olingo.server.api.serializer.SerializerException;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+
+public class MetadataDocumentJsonSerializer {
+  
+  private final ServiceMetadata serviceMetadata;
+  private final Map<String, String> namespaceToAlias = new HashMap<String, 
String>();
+  private static final String DOLLAR = "$";
+  private static final String VERSION = DOLLAR + "Version";
+  private static final String REFERENCES = DOLLAR + "Reference";
+  private static final String INCLUDE = DOLLAR + "Include";
+  private static final String NAMESPACE = DOLLAR + "Namespace";
+  private static final String ALIAS = DOLLAR + "Alias";
+  private static final String INCLUDE_ANNOTATIONS = DOLLAR + 
"IncludeAnnotations";
+  private static final String TERM_NAMESPACE = DOLLAR + "TermNamespace";
+  private static final String TARGET_NAMESPACE = DOLLAR + "TargetNamespace";
+  private static final String QUALIFIER = DOLLAR + "Qualifier";
+  private static final String IS_FLAGS = DOLLAR + "IsFlags";
+  private static final String UNDERLYING_TYPE = DOLLAR + "UnderlyingType";
+  private static final String KIND = DOLLAR + "Kind";
+  private static final String MAX_LENGTH = DOLLAR + "MaxLength";
+  private static final String PRECISION = DOLLAR + "Precision";
+  private static final String SCALE = DOLLAR + "Scale";
+  private static final String SRID = DOLLAR + "SRID";
+  private static final String COLLECTION = DOLLAR + "Collection";
+  private static final String BASE_TYPE = DOLLAR + "BaseType";
+  private static final String HAS_STREAM = DOLLAR + "HasStream";
+  private static final String KEY = DOLLAR + "Key";
+  private static final String ABSTRACT = DOLLAR + "Abstract";
+  private static final String TYPE = DOLLAR + "Type";
+  private static final String NULLABLE = DOLLAR + "Nullable";
+  private static final String UNICODE = DOLLAR + "Unicode";
+  private static final String DEFAULT_VALUE = DOLLAR + "DefaultValue";
+  private static final String PARTNER = DOLLAR + "Partner";
+  private static final String CONTAINS_TARGET = DOLLAR + "ContainsTarget";
+  private static final String REFERENTIAL_CONSTRAINT = DOLLAR + 
"ReferentialConstraint";
+  private static final String ISBOUND = DOLLAR + "IsBound";
+  private static final String ENTITY_SET_PATH = DOLLAR + "EntitySetPath";
+  private static final String PARAMETER = DOLLAR + "Parameter";
+  private static final String RETURN_TYPE = DOLLAR + "ReturnType";
+  private static final String ISCOMPOSABLE = DOLLAR + "IsComposable";
+  private static final String PARAMETER_NAME = DOLLAR + "Name";
+  private static final String BASE_TERM = DOLLAR + "BaseTerm";
+  private static final String APPLIES_TO = DOLLAR + "AppliesTo";
+  private static final String NAVIGATION_PROPERTY_BINDING = DOLLAR + 
"NavigationPropertyBinding";
+  private static final String EXTENDS = DOLLAR + "Extends";
+  private static final String INCLUDE_IN_SERV_DOC = DOLLAR + 
"IncludeInServiceDocument";
+  private static final String ANNOTATION = DOLLAR + "Annotations";
+  private static final String ANNOTATION_PATH = DOLLAR + "Path";
+  private static final String NAME = DOLLAR + "Name";
+
+  public MetadataDocumentJsonSerializer(final ServiceMetadata serviceMetadata) 
throws SerializerException {
+    if (serviceMetadata == null || serviceMetadata.getEdm() == null) {
+      throw new SerializerException("Service Metadata and EDM must not be null 
for a service.",
+          SerializerException.MessageKeys.NULL_METADATA_OR_EDM);
+    }
+    this.serviceMetadata = serviceMetadata;
+  }
+  
+  public void writeMetadataDocument(final JsonGenerator json) throws 
SerializerException, IOException {
+    json.writeStartObject();
+    json.writeStringField(VERSION, "4.01");
+    if (!serviceMetadata.getReferences().isEmpty()) {
+      appendReference(json);
+    }
+    appendDataServices(json);
+    json.writeEndObject();
+  }
+
+  private void appendDataServices(JsonGenerator json) throws 
SerializerException, IOException {
+    for (EdmSchema schema : serviceMetadata.getEdm().getSchemas()) {
+      appendSchema(json, schema);
+    }
+  }
+
+  private void appendSchema(JsonGenerator json, EdmSchema schema) 
+      throws SerializerException, IOException {
+    json.writeFieldName(schema.getNamespace());
+    json.writeStartObject();
+    if (schema.getAlias() != null) {
+      json.writeStringField(ALIAS, schema.getAlias());
+      namespaceToAlias.put(schema.getNamespace(), schema.getAlias());
+    }
+    // EnumTypes
+    appendEnumTypes(json, schema.getEnumTypes());
+    
+    // TypeDefinitions
+    appendTypeDefinitions(json, schema.getTypeDefinitions());
+    
+    // EntityTypes
+    appendEntityTypes(json, schema.getEntityTypes());
+    
+    // ComplexTypes
+    appendComplexTypes(json, schema.getComplexTypes());
+    
+    // Actions
+    appendActions(json, schema.getActions());
+    
+    // Functions
+    appendFunctions(json, schema.getFunctions());
+    
+    //Terms
+    appendTerms(json, schema.getTerms());
+    
+    // EntityContainer
+    appendEntityContainer(json, schema.getEntityContainer());
+ 
+    // AnnotationGroups
+    appendAnnotationGroups(json, schema.getAnnotationGroups());
+
+    appendAnnotations(json, schema, null);
+    
+    json.writeEndObject();
+  }
+
+  private void appendAnnotationGroups(final JsonGenerator json, 
+      final List<EdmAnnotations> annotationGroups) throws SerializerException, 
IOException {
+    if (annotationGroups.size() > 0) {
+      json.writeObjectFieldStart(ANNOTATION);
+    }
+    for (EdmAnnotations annotationGroup : annotationGroups) {
+      appendAnnotationGroup(json, annotationGroup);
+    }
+    if (annotationGroups.size() > 0) {
+      json.writeEndObject();
+    }
+  }
+
+  private void appendAnnotationGroup(final JsonGenerator json, 
+      final EdmAnnotations annotationGroup) throws SerializerException, 
IOException {
+    String targetPath = annotationGroup.getTargetPath();
+    if (annotationGroup.getQualifier() != null) {
+      json.writeObjectFieldStart(targetPath + "#" + 
annotationGroup.getQualifier());
+    } else {
+      json.writeObjectFieldStart(targetPath);
+    }
+    appendAnnotations(json, annotationGroup, null);
+    json.writeEndObject();
+  }
+
+  private void appendEntityContainer(final JsonGenerator json, 
+      final EdmEntityContainer container) throws SerializerException, 
IOException {
+    if (container != null) {
+      json.writeObjectFieldStart(container.getName());
+      json.writeStringField(KIND, Kind.EntityContainer.name());
+      FullQualifiedName parentContainerName = 
container.getParentContainerName();
+      if (parentContainerName != null) {
+        String parentContainerNameString;
+        if (namespaceToAlias.get(parentContainerName.getNamespace()) != null) {
+          parentContainerNameString =
+              namespaceToAlias.get(parentContainerName.getNamespace()) + "." + 
parentContainerName.getName();
+        } else {
+          parentContainerNameString = 
parentContainerName.getFullQualifiedNameAsString();
+        }
+        json.writeObjectFieldStart(Kind.Extending.name());
+        json.writeStringField(KIND, Kind.EntityContainer.name());
+        json.writeStringField(EXTENDS, parentContainerNameString);
+        json.writeEndObject();
+      }
+
+      // EntitySets
+      appendEntitySets(json, container.getEntitySets());
+
+      String containerNamespace;
+      if (namespaceToAlias.get(container.getNamespace()) != null) {
+        containerNamespace = namespaceToAlias.get(container.getNamespace());
+      } else {
+        containerNamespace = container.getNamespace();
+      }
+      // ActionImports
+      appendActionImports(json, container.getActionImports(), 
containerNamespace);
+     
+      // FunctionImports
+      appendFunctionImports(json, container.getFunctionImports(), 
containerNamespace);
+
+       
+      // Singletons
+      appendSingletons(json, container.getSingletons());
+
+      // Annotations
+      appendAnnotations(json, container, null);
+
+      json.writeEndObject();
+    }
+    
+  }
+
+  private void appendSingletons(final JsonGenerator json, 
+      final List<EdmSingleton> singletons) throws SerializerException, 
IOException {
+    for (EdmSingleton singleton : singletons) {
+      json.writeObjectFieldStart(singleton.getName());
+      json.writeStringField(KIND, Kind.Singleton.name());
+      json.writeStringField(TYPE, 
getAliasedFullQualifiedName(singleton.getEntityType()));
+      
+      appendNavigationPropertyBindings(json, singleton);
+      appendAnnotations(json, singleton, null);
+      json.writeEndObject();
+    }
+  }
+
+  private void appendFunctionImports(final JsonGenerator json, final 
List<EdmFunctionImport> functionImports,
+      final String containerNamespace) throws SerializerException, IOException 
{
+    for (EdmFunctionImport functionImport : functionImports) {
+      json.writeObjectFieldStart(functionImport.getName());
+
+      json.writeStringField(KIND, Kind.FunctionImport.name());
+      String functionFQNString;
+      FullQualifiedName functionFqn = functionImport.getFunctionFqn();
+      if (namespaceToAlias.get(functionFqn.getNamespace()) != null) {
+        functionFQNString = namespaceToAlias.get(functionFqn.getNamespace()) + 
"." + functionFqn.getName();
+      } else {
+        functionFQNString = functionFqn.getFullQualifiedNameAsString();
+      }
+      json.writeStringField(DOLLAR + Kind.Function.name(), functionFQNString);
+
+      EdmEntitySet returnedEntitySet = functionImport.getReturnedEntitySet();
+      if (returnedEntitySet != null) {
+        json.writeStringField(DOLLAR + Kind.EntitySet.name(), 
+            containerNamespace + "." + returnedEntitySet.getName());
+      }
+      // Default is false and we do not write the default
+      if (functionImport.isIncludeInServiceDocument()) {
+        json.writeBooleanField(INCLUDE_IN_SERV_DOC, 
functionImport.isIncludeInServiceDocument());
+      }
+      appendAnnotations(json, functionImport, null);
+      json.writeEndObject();
+    }
+  }
+
+  private void appendActionImports(final JsonGenerator json, 
+      final List<EdmActionImport> actionImports, String containerNamespace) 
+          throws SerializerException, IOException {
+    for (EdmActionImport actionImport : actionImports) {
+      json.writeObjectFieldStart(actionImport.getName());
+      json.writeStringField(KIND, Kind.ActionImport.name());
+      json.writeStringField(DOLLAR + Kind.Action.name(), 
getAliasedFullQualifiedName(actionImport.getUnboundAction()));
+      if (actionImport.getReturnedEntitySet() != null) {
+        json.writeStringField(DOLLAR + Kind.EntitySet.name(), 
+            containerNamespace + "." + 
actionImport.getReturnedEntitySet().getName());
+      }
+      appendAnnotations(json, actionImport, null);
+      json.writeEndObject();
+    }
+    
+  }
+
+  private void appendEntitySets(final JsonGenerator json, 
+      final List<EdmEntitySet> entitySets) throws SerializerException, 
IOException {
+    for (EdmEntitySet entitySet : entitySets) {
+      json.writeObjectFieldStart(entitySet.getName());
+      json.writeStringField(KIND, Kind.EntitySet.name());
+      json.writeStringField(TYPE, 
getAliasedFullQualifiedName(entitySet.getEntityType()));
+      if (!entitySet.isIncludeInServiceDocument()) {
+        json.writeBooleanField(INCLUDE_IN_SERV_DOC, 
entitySet.isIncludeInServiceDocument());
+      }
+
+      appendNavigationPropertyBindings(json, entitySet);
+      appendAnnotations(json, entitySet, null);
+      json.writeEndObject();
+    }
+  }
+
+  private void appendNavigationPropertyBindings(final JsonGenerator json, 
+      final EdmBindingTarget bindingTarget) throws SerializerException, 
IOException {
+    if (bindingTarget.getNavigationPropertyBindings() != null && 
+        bindingTarget.getNavigationPropertyBindings().size() > 0) {
+      json.writeObjectFieldStart(NAVIGATION_PROPERTY_BINDING);
+      for (EdmNavigationPropertyBinding binding : 
bindingTarget.getNavigationPropertyBindings()) {
+        json.writeStringField(binding.getPath(), binding.getTarget());
+      }
+      json.writeEndObject();
+    }
+  }
+
+  private void appendTerms(final JsonGenerator json, final List<EdmTerm> 
terms) 
+      throws SerializerException, IOException {
+    for (EdmTerm term : terms) {
+      json.writeObjectFieldStart(term.getName());
+      json.writeStringField(KIND, Kind.Term.name());
+
+      json.writeStringField(TYPE, getAliasedFullQualifiedName(term.getType()));
+
+      if (term.getBaseTerm() != null) {
+        json.writeStringField(BASE_TERM, 
getAliasedFullQualifiedName(term.getBaseTerm().getFullQualifiedName()));
+      }
+
+      if (term.getAppliesTo() != null && !term.getAppliesTo().isEmpty()) {
+        String appliesToString = "";
+        boolean first = true;
+        for (TargetType target : term.getAppliesTo()) {
+          if (first) {
+            first = false;
+            appliesToString = target.toString();
+          } else {
+            appliesToString = appliesToString + " " + target.toString();
+          }
+        }
+        json.writeStringField(APPLIES_TO, appliesToString);
+      }
+
+      // Facets
+      if (!term.isNullable()) {
+        json.writeBooleanField(NULLABLE, term.isNullable());
+      }
+
+      if (term.getDefaultValue() != null) {
+        json.writeStringField(DEFAULT_VALUE, term.getDefaultValue());
+      }
+
+      if (term.getMaxLength() != null) {
+        json.writeNumberField(MAX_LENGTH, term.getMaxLength());
+      }
+
+      if (term.getPrecision() != null) {
+        json.writeNumberField(PRECISION, term.getPrecision());
+      }
+
+      if (term.getScale() != null) {
+        json.writeNumberField(SCALE, term.getScale());
+      }
+      
+      appendAnnotations(json, term, null);
+      json.writeEndObject();
+    }
+    
+  }
+
+  private void appendFunctions(final JsonGenerator json, 
+      final List<EdmFunction> functions) throws SerializerException, 
IOException {
+    Map<String, List<EdmFunction>> functionsMap = new HashMap<String, 
List<EdmFunction>>();
+    for (EdmFunction function : functions) {
+      if (functionsMap.containsKey(function.getName())) {
+        List<EdmFunction> actionsWithSpecificActionName = 
functionsMap.get(function.getName());
+        actionsWithSpecificActionName.add(function);
+        functionsMap.put(function.getName(), actionsWithSpecificActionName);
+      } else {
+        List<EdmFunction> functionList = new ArrayList<EdmFunction>();
+        functionList.add(function);
+        functionsMap.put(function.getName(), functionList);
+      }
+    }
+    
+    for (Entry<String, List<EdmFunction>> functionsMapEntry : 
functionsMap.entrySet()) {
+      json.writeArrayFieldStart(functionsMapEntry.getKey());
+      List<EdmFunction> functionEntry = functionsMapEntry.getValue();
+      for (EdmFunction function : functionEntry) {
+        json.writeStartObject();
+        json.writeStringField(KIND, Kind.Function.name());
+        if (function.getEntitySetPath() != null) {
+          json.writeStringField(ENTITY_SET_PATH, function.getEntitySetPath());
+        }
+        if (function.isBound()) {
+          json.writeBooleanField(ISBOUND, function.isBound());
+        }
+
+        if (function.isComposable()) {
+          json.writeBooleanField(ISCOMPOSABLE, function.isComposable());
+        }
+        
+        appendOperationParameters(json, function);
+
+        appendOperationReturnType(json, function);
+
+        appendAnnotations(json, function, null);
+
+        json.writeEndObject();
+      }
+      json.writeEndArray();
+    }
+  }
+
+  private void appendActions(final JsonGenerator json, 
+      final List<EdmAction> actions) throws SerializerException, IOException {
+    Map<String, List<EdmAction>> actionsMap = new HashMap<String, 
List<EdmAction>>();
+    for (EdmAction action : actions) {
+      if (actionsMap.containsKey(action.getName())) {
+        List<EdmAction> actionsWithSpecificActionName = 
actionsMap.get(action.getName());
+        actionsWithSpecificActionName.add(action);
+        actionsMap.put(action.getName(), actionsWithSpecificActionName);
+      } else {
+        List<EdmAction> actionList = new ArrayList<EdmAction>();
+        actionList.add(action);
+        actionsMap.put(action.getName(), actionList);
+      }
+    }
+    for (Entry<String, List<EdmAction>> actionsMapEntry : 
actionsMap.entrySet()) {
+      json.writeArrayFieldStart(actionsMapEntry.getKey());
+      List<EdmAction> actionEntry = actionsMapEntry.getValue();
+      for (EdmAction action : actionEntry) {
+        json.writeStartObject();
+        json.writeStringField(KIND, Kind.Action.name());
+        if (action.getEntitySetPath() != null) {
+          json.writeStringField(ENTITY_SET_PATH, action.getEntitySetPath());
+        }
+        json.writeBooleanField(ISBOUND, action.isBound());
+
+        appendOperationParameters(json, action);
+
+        appendOperationReturnType(json, action);
+
+        appendAnnotations(json, action, null);
+
+        json.writeEndObject();
+      }
+      json.writeEndArray();
+    }
+  }
+
+  private void appendOperationReturnType(final JsonGenerator json, 
+      final EdmOperation operation) throws SerializerException, IOException {
+    EdmReturnType returnType = operation.getReturnType();
+    if (returnType != null) {
+      json.writeObjectFieldStart(RETURN_TYPE);
+      String returnTypeFqnString;
+      if (EdmTypeKind.PRIMITIVE.equals(returnType.getType().getKind())) {
+        returnTypeFqnString = getFullQualifiedName(returnType.getType());
+      } else {
+        returnTypeFqnString = 
getAliasedFullQualifiedName(returnType.getType());
+      }
+      json.writeStringField(TYPE, returnTypeFqnString);
+      if (returnType.isCollection()) {
+        json.writeBooleanField(COLLECTION, returnType.isCollection());
+      }
+      
+      appendReturnTypeFacets(json, returnType);
+      json.writeEndObject();
+    }
+  }
+
+  private void appendReturnTypeFacets(final JsonGenerator json, 
+      final EdmReturnType returnType) throws SerializerException, IOException {
+    if (!returnType.isNullable()) {
+      json.writeBooleanField(NULLABLE, returnType.isNullable());
+    }
+    if (returnType.getMaxLength() != null) {
+      json.writeNumberField(MAX_LENGTH, returnType.getMaxLength());
+    }
+    if (returnType.getPrecision() != null) {
+      json.writeNumberField(PRECISION, returnType.getPrecision());
+    }
+    if (returnType.getScale() != null) {
+      json.writeNumberField(SCALE, returnType.getScale());
+    }
+  }
+
+  private void appendOperationParameters(final JsonGenerator json, 
+      final EdmOperation operation) throws SerializerException, IOException {
+    if (!operation.getParameterNames().isEmpty()) {
+      json.writeArrayFieldStart(PARAMETER);
+    }
+    for (String parameterName : operation.getParameterNames()) {
+      EdmParameter parameter = operation.getParameter(parameterName);
+      json.writeStartObject();
+      json.writeStringField(PARAMETER_NAME, parameterName);
+      String typeFqnString;
+      if (EdmTypeKind.PRIMITIVE.equals(parameter.getType().getKind())) {
+        typeFqnString = getFullQualifiedName(parameter.getType());
+      } else {
+        typeFqnString = getAliasedFullQualifiedName(parameter.getType());
+      }
+      json.writeStringField(TYPE, typeFqnString);
+      if (parameter.isCollection()) {
+        json.writeBooleanField(COLLECTION, parameter.isCollection());
+      }
+      
+      appendParameterFacets(json, parameter);
+
+      appendAnnotations(json, parameter, null);
+      json.writeEndObject();
+    }
+    if (!operation.getParameterNames().isEmpty()) {
+      json.writeEndArray();
+    }
+  }
+
+  private void appendParameterFacets(final JsonGenerator json, 
+      final EdmParameter parameter) throws SerializerException, IOException {
+    if (!parameter.isNullable()) {
+      json.writeBooleanField(NULLABLE, parameter.isNullable());
+    }
+    if (parameter.getMaxLength() != null) {
+      json.writeNumberField(MAX_LENGTH, parameter.getMaxLength());
+    }
+    if (parameter.getPrecision() != null) {
+      json.writeNumberField(PRECISION, parameter.getPrecision());
+    }
+    if (parameter.getScale() != null) {
+      json.writeNumberField(SCALE, parameter.getScale());
+    }
+  }
+
+  private void appendComplexTypes(final JsonGenerator json, 
+      final List<EdmComplexType> complexTypes) throws SerializerException, 
IOException {
+    for (EdmComplexType complexType : complexTypes) {
+      json.writeObjectFieldStart(complexType.getName());
+
+      json.writeStringField(KIND, Kind.ComplexType.name());
+      if (complexType.getBaseType() != null) {
+        json.writeStringField(BASE_TYPE, 
getAliasedFullQualifiedName(complexType.getBaseType()));
+      }
+
+      if (complexType.isAbstract()) {
+        json.writeBooleanField(ABSTRACT, complexType.isAbstract());
+      }
+
+      appendProperties(json, complexType);
+
+      appendNavigationProperties(json, complexType);
+
+      appendAnnotations(json, complexType, null);
+
+      json.writeEndObject();
+    }
+  }
+
+  private void appendEntityTypes(JsonGenerator json, 
+      List<EdmEntityType> entityTypes) throws SerializerException, IOException 
{
+    for (EdmEntityType entityType : entityTypes) {
+      json.writeObjectFieldStart(entityType.getName());
+      json.writeStringField(KIND, Kind.EntityType.name());
+      if (entityType.hasStream()) {
+        json.writeBooleanField(HAS_STREAM, entityType.hasStream());
+      }
+
+      if (entityType.getBaseType() != null) {
+        json.writeStringField(BASE_TYPE, 
getAliasedFullQualifiedName(entityType.getBaseType()));
+      }
+
+      if (entityType.isAbstract()) {
+        json.writeBooleanField(ABSTRACT, entityType.isAbstract());
+      }
+
+      appendKey(json, entityType);
+
+      appendProperties(json, entityType);
+
+      appendNavigationProperties(json, entityType);
+
+      appendAnnotations(json, entityType, null);
+
+      json.writeEndObject();
+    }
+  }
+
+  private void appendNavigationProperties(final JsonGenerator json, 
+      final EdmStructuredType type) throws SerializerException, IOException {
+    List<String> navigationPropertyNames = new 
ArrayList<String>(type.getNavigationPropertyNames());
+    if (type.getBaseType() != null) {
+      
navigationPropertyNames.removeAll(type.getBaseType().getNavigationPropertyNames());
+    }
+    for (String navigationPropertyName : navigationPropertyNames) {
+      EdmNavigationProperty navigationProperty = 
type.getNavigationProperty(navigationPropertyName);
+      json.writeObjectFieldStart(navigationPropertyName);
+      json.writeStringField(KIND, Kind.NavigationProperty.name());
+      
+      json.writeStringField(TYPE, 
getAliasedFullQualifiedName(navigationProperty.getType()));
+      if (navigationProperty.isCollection()) {
+        json.writeBooleanField(COLLECTION, navigationProperty.isCollection());
+      }
+      
+      if (!navigationProperty.isNullable()) {
+        json.writeBooleanField(NULLABLE, navigationProperty.isNullable());
+      }
+
+      if (navigationProperty.getPartner() != null) {
+        EdmNavigationProperty partner = navigationProperty.getPartner();
+        json.writeStringField(PARTNER, partner.getName());
+      }
+
+      if (navigationProperty.containsTarget()) {
+        json.writeBooleanField(CONTAINS_TARGET, 
navigationProperty.containsTarget());
+      }
+
+      if (navigationProperty.getReferentialConstraints() != null) {
+        for (EdmReferentialConstraint constraint : 
navigationProperty.getReferentialConstraints()) {
+          json.writeObjectFieldStart(REFERENTIAL_CONSTRAINT);
+          json.writeStringField(constraint.getPropertyName(), 
constraint.getReferencedPropertyName());
+          for (EdmAnnotation annotation : constraint.getAnnotations()) {
+            appendAnnotations(json, annotation, null);
+          }
+          json.writeEndObject();
+        }
+      }
+
+      appendAnnotations(json, navigationProperty, null);
+
+      json.writeEndObject();
+    }
+  }
+
+  private void appendProperties(final JsonGenerator json, 
+      final EdmStructuredType type) throws SerializerException, IOException {
+    List<String> propertyNames = new 
ArrayList<String>(type.getPropertyNames());
+    if (type.getBaseType() != null) {
+      propertyNames.removeAll(type.getBaseType().getPropertyNames());
+    }
+    for (String propertyName : propertyNames) {
+      EdmProperty property = type.getStructuralProperty(propertyName);
+      json.writeObjectFieldStart(propertyName);
+      String fqnString;
+      if (property.isPrimitive()) {
+        fqnString = getFullQualifiedName(property.getType());
+      } else {
+        fqnString = getAliasedFullQualifiedName(property.getType());
+      }
+      json.writeStringField(TYPE, fqnString);
+      if (property.isCollection()) {
+        json.writeBooleanField(COLLECTION, property.isCollection());
+      }
+
+      // Facets
+      if (!property.isNullable()) {
+        json.writeBooleanField(NULLABLE, property.isNullable());
+      }
+
+      if (!property.isUnicode()) {
+        json.writeBooleanField(UNICODE, property.isUnicode());
+      }
+
+      if (property.getDefaultValue() != null) {
+        json.writeStringField(DEFAULT_VALUE, property.getDefaultValue());
+      }
+
+      if (property.getMaxLength() != null) {
+        json.writeNumberField(MAX_LENGTH, property.getMaxLength());
+      }
+
+      if (property.getPrecision() != null) {
+        json.writeNumberField(PRECISION, property.getPrecision());
+      }
+
+      if (property.getScale() != null) {
+        json.writeNumberField(SCALE, property.getScale());
+      }
+
+      appendAnnotations(json, property, null);
+      json.writeEndObject();
+    }
+  }
+
+  private void appendKey(final JsonGenerator json, 
+      final EdmEntityType entityType) throws SerializerException, IOException {
+    List<EdmKeyPropertyRef> keyPropertyRefs = entityType.getKeyPropertyRefs();
+    if (keyPropertyRefs != null && !keyPropertyRefs.isEmpty()) {
+      // Resolve Base Type key as it is shown in derived type
+      EdmEntityType baseType = entityType.getBaseType();
+      if (baseType != null && baseType.getKeyPropertyRefs() != null && 
!(baseType.getKeyPropertyRefs().isEmpty())) {
+        return;
+      }
+      json.writeArrayFieldStart(KEY);
+      for (EdmKeyPropertyRef keyRef : keyPropertyRefs) {
+        
+        if (keyRef.getAlias() != null) {
+          json.writeStartObject();
+          json.writeStringField(keyRef.getAlias(), keyRef.getName());
+          json.writeEndObject();
+        } else {
+          json.writeString(keyRef.getName());
+        }
+      }
+      json.writeEndArray();
+    }
+  }
+
+  private String getAliasedFullQualifiedName(final EdmType type) {
+    FullQualifiedName fqn = type.getFullQualifiedName();
+    return getAliasedFullQualifiedName(fqn);
+  }
+  
+  private void appendTypeDefinitions(final JsonGenerator json, 
+      final List<EdmTypeDefinition> typeDefinitions) throws 
SerializerException, IOException {
+    for (EdmTypeDefinition definition : typeDefinitions) {
+      json.writeObjectFieldStart(definition.getName());
+      json.writeStringField(KIND, definition.getKind().name());
+      json.writeStringField(UNDERLYING_TYPE, 
getFullQualifiedName(definition.getUnderlyingType()));
+      
+      // Facets
+      if (definition.getMaxLength() != null) {
+        json.writeStringField(MAX_LENGTH, "" + definition.getMaxLength());
+      }
+
+      if (definition.getPrecision() != null) {
+        json.writeStringField(PRECISION, "" + definition.getPrecision());
+      }
+
+      if (definition.getScale() != null) {
+        json.writeStringField(SCALE, "" + definition.getScale());
+      }
+      
+      if (definition.getSrid() != null) {
+        json.writeStringField(SRID, "" + definition.getSrid());
+      }
+
+      appendAnnotations(json, definition, null);
+      json.writeEndObject();
+    }
+  }
+
+  private void appendEnumTypes(JsonGenerator json, List<EdmEnumType> 
enumTypes) 
+      throws SerializerException, IOException {
+    for (EdmEnumType enumType : enumTypes) {
+      json.writeObjectFieldStart(enumType.getName());
+      json.writeStringField(KIND, Kind.EnumType.name());
+      json.writeBooleanField(IS_FLAGS, enumType.isFlags());
+      json.writeStringField(UNDERLYING_TYPE, 
getFullQualifiedName(enumType.getUnderlyingType()));
+
+      for (String memberName : enumType.getMemberNames()) {
+
+        EdmMember member = enumType.getMember(memberName);
+        if (member.getValue() != null) {
+          json.writeStringField(memberName, member.getValue());
+        }
+
+        appendAnnotations(json, member, memberName);
+      }
+      json.writeEndObject();
+    }
+  }
+  
+  private void appendAnnotations(JsonGenerator json, 
+      final EdmAnnotatable annotatable, String memberName) throws 
SerializerException, IOException {
+    List<EdmAnnotation> annotations = annotatable.getAnnotations();
+    if (annotations != null && !annotations.isEmpty()) {
+      for (EdmAnnotation annotation : annotations) {
+        String termName = memberName != null ? memberName : "";
+        if (annotation.getTerm() != null) {
+          termName += "@" + 
getAliasedFullQualifiedName(annotation.getTerm().getFullQualifiedName());
+        }
+        if (annotation.getQualifier() != null) {
+          termName += "#" + annotation.getQualifier();
+        } 
+        if (annotation.getExpression() == null && termName.length() > 0) {
+          json.writeBooleanField(termName, true);
+        } else {
+          appendExpression(json, annotation.getExpression(), termName);
+        }
+        appendAnnotations(json, annotation, termName);
+      }
+    }
+  }
+  
+  private void appendExpression(final JsonGenerator json,
+      final EdmExpression expression, String termName) throws 
SerializerException, IOException {
+    if (expression == null) {
+      return;
+    }
+    if (expression.isConstant()) {
+      appendConstantExpression(json, expression.asConstant(), termName);
+    } else if (expression.isDynamic()) {
+      appendDynamicExpression(json, expression.asDynamic(), termName);
+    } else {
+      throw new IllegalArgumentException("Unkown expressiontype in metadata");
+    }
+  }
+  
+  private void appendDynamicExpression(JsonGenerator json, 
+      EdmDynamicExpression dynExp, String termName) throws 
SerializerException, IOException {
+    json.writeFieldName(termName);
+    switch (dynExp.getExpressionType()) {
+    // Logical
+    case And:
+      appendLogicalOrComparisonExpression(json, dynExp.asAnd());
+      break;
+    case Or:
+      appendLogicalOrComparisonExpression(json, dynExp.asOr());
+      break;
+    case Not:
+      appendNotExpression(json, dynExp.asNot());
+      break;
+    // Comparison
+    case Eq:
+      appendLogicalOrComparisonExpression(json, dynExp.asEq());
+      break;
+    case Ne:
+      appendLogicalOrComparisonExpression(json, dynExp.asNe());
+      break;
+    case Gt:
+      appendLogicalOrComparisonExpression(json, dynExp.asGt());
+      break;
+    case Ge:
+      appendLogicalOrComparisonExpression(json, dynExp.asGe());
+      break;
+    case Lt:
+      appendLogicalOrComparisonExpression(json, dynExp.asLt());
+      break;
+    case Le:
+      appendLogicalOrComparisonExpression(json, dynExp.asLe());
+      break;
+    case AnnotationPath:
+      json.writeStartObject();
+      json.writeStringField(ANNOTATION_PATH, 
dynExp.asAnnotationPath().getValue());
+      json.writeEndObject();
+      break;
+    case Apply:
+      EdmApply asApply = dynExp.asApply();
+      json.writeStartObject();
+      json.writeArrayFieldStart(DOLLAR + asApply.getExpressionName());
+      for (EdmExpression parameter : asApply.getParameters()) {
+        appendExpression(json, parameter, null);
+      }
+      json.writeEndArray();
+      json.writeStringField(DOLLAR + Kind.Function.name(), 
asApply.getFunction());
+      
+      appendAnnotations(json, asApply, null);
+      json.writeEndObject();
+      break;
+    case Cast:
+      EdmCast asCast = dynExp.asCast();
+      json.writeStartObject();
+      appendExpression(json, asCast.getValue(), DOLLAR + 
asCast.getExpressionName());
+      json.writeStringField(TYPE, 
getAliasedFullQualifiedName(asCast.getType()));
+     
+      if (asCast.getMaxLength() != null) {
+        json.writeNumberField(MAX_LENGTH, asCast.getMaxLength());
+      }
+
+      if (asCast.getPrecision() != null) {
+        json.writeNumberField(PRECISION, asCast.getPrecision());
+      }
+
+      if (asCast.getScale() != null) {
+        json.writeNumberField(SCALE, asCast.getScale());
+      }
+      appendAnnotations(json, asCast, null);
+      json.writeEndObject();
+      break;
+    case Collection:
+      json.writeStartArray();
+      for (EdmExpression item : dynExp.asCollection().getItems()) {
+        appendExpression(json, item, null);
+      }
+      json.writeEndArray();
+      break;
+    case If:
+      EdmIf asIf = dynExp.asIf();
+      json.writeStartObject();
+      json.writeArrayFieldStart(DOLLAR + asIf.getExpressionName());
+      appendExpression(json, asIf.getGuard(), null);
+      appendExpression(json, asIf.getThen(), null);
+      appendExpression(json, asIf.getElse(), null);
+      json.writeEndArray();
+      appendAnnotations(json, asIf, null);
+      json.writeEndObject();
+      break;
+    case IsOf:
+      EdmIsOf asIsOf = dynExp.asIsOf();
+      json.writeStartObject();
+      appendExpression(json, asIsOf.getValue(), DOLLAR + 
asIsOf.getExpressionName());
+      
+      json.writeStringField(TYPE, 
getAliasedFullQualifiedName(asIsOf.getType()));
+
+      if (asIsOf.getMaxLength() != null) {
+        json.writeNumberField(MAX_LENGTH, asIsOf.getMaxLength());
+      }
+
+      if (asIsOf.getPrecision() != null) {
+        json.writeNumberField(PRECISION, asIsOf.getPrecision());
+      }
+
+      if (asIsOf.getScale() != null) {
+        json.writeNumberField(SCALE, asIsOf.getScale());
+      }
+      appendAnnotations(json, asIsOf, null);
+      json.writeEndObject();
+      break;
+    case LabeledElement:
+      EdmLabeledElement asLabeledElement = dynExp.asLabeledElement();
+      json.writeStartObject();
+      appendExpression(json, asLabeledElement.getValue(), DOLLAR + 
asLabeledElement.getExpressionName());
+      json.writeStringField(NAME, asLabeledElement.getName());
+      appendAnnotations(json, asLabeledElement, null);
+      json.writeEndObject();
+      break;
+    case LabeledElementReference:
+      EdmLabeledElementReference asLabeledElementReference = 
dynExp.asLabeledElementReference();
+      json.writeStartObject();
+      json.writeStringField(DOLLAR + 
asLabeledElementReference.getExpressionName(), 
+          asLabeledElementReference.getValue());
+      json.writeEndObject();
+      break;
+    case Null:
+      EdmNull asNull = dynExp.asNull();
+      json.writeStartObject();
+      json.writeStringField(DOLLAR + asNull.getExpressionName(), null);
+      appendAnnotations(json, dynExp.asNull(), null);
+      json.writeEndObject();
+      break;
+    case NavigationPropertyPath:
+      EdmNavigationPropertyPath asNavigationPropertyPath = 
dynExp.asNavigationPropertyPath();
+      json.writeStartObject();
+      json.writeStringField(DOLLAR + 
asNavigationPropertyPath.getExpressionName(), 
+          asNavigationPropertyPath.getValue());
+      json.writeEndObject();
+      break;
+    case Path:
+      EdmPath asPath = dynExp.asPath();
+      json.writeStartObject();
+      json.writeStringField(DOLLAR + asPath.getExpressionName(), 
asPath.getValue());
+      json.writeEndObject();
+      break;
+    case PropertyPath:
+      EdmPropertyPath asPropertyPath = dynExp.asPropertyPath();
+      json.writeStartObject();
+      json.writeStringField(DOLLAR + asPropertyPath.getExpressionName(), 
asPropertyPath.getValue());
+      json.writeEndObject();
+      break;
+    case Record:
+      EdmRecord asRecord = dynExp.asRecord();
+      json.writeStartObject();
+      EdmStructuredType type = asRecord.getType();
+      if (type != null) {
+        json.writeStringField(TYPE, getAliasedFullQualifiedName(type));
+      }
+      for (EdmPropertyValue propValue : asRecord.getPropertyValues()) {
+        appendExpression(json, propValue.getValue(), propValue.getProperty());
+        appendAnnotations(json, propValue, propValue.getProperty());
+      }
+      appendAnnotations(json, asRecord, null);
+      json.writeEndObject();
+      break;
+    case UrlRef:
+      EdmUrlRef asUrlRef = dynExp.asUrlRef();
+      json.writeStartObject();
+      appendExpression(json, asUrlRef.getValue(), DOLLAR + 
asUrlRef.getExpressionName());
+      appendAnnotations(json, asUrlRef, null);
+      json.writeEndObject();
+      break;
+    default:
+      throw new IllegalArgumentException("Unkown ExpressionType for dynamic 
expression: " + dynExp.getExpressionType());
+    }
+  }
+
+  private void appendNotExpression(final JsonGenerator json, final EdmNot exp) 
+      throws SerializerException, IOException {
+    json.writeStartObject();
+    appendExpression(json, exp.getLeftExpression(), DOLLAR + 
exp.getExpressionName());
+    appendAnnotations(json, exp, null);
+    json.writeEndObject();
+  }
+
+  private void appendLogicalOrComparisonExpression(final JsonGenerator json, 
+      final EdmLogicalOrComparisonExpression exp) throws SerializerException, 
IOException {
+    json.writeStartObject();
+    json.writeArrayFieldStart(DOLLAR + exp.getExpressionName());
+    appendExpression(json, exp.getLeftExpression(), null);
+    appendExpression(json, exp.getRightExpression(), null);
+    json.writeEndArray();
+    appendAnnotations(json, exp, null);
+    json.writeEndObject();
+  }
+
+  private void appendConstantExpression(final JsonGenerator json, 
+      final EdmConstantExpression constExp, String termName) throws 
SerializerException, IOException {
+    switch (constExp.getExpressionType()) {
+    case Binary: 
+      json.writeObjectFieldStart(termName);
+      json.writeStringField(DOLLAR + constExp.getExpressionName(), 
constExp.getValueAsString());
+      json.writeEndObject();
+      break;
+    case Date:
+      json.writeObjectFieldStart(termName);
+      json.writeStringField(DOLLAR + constExp.getExpressionName(), 
constExp.getValueAsString());
+      json.writeEndObject();
+      break;
+    case DateTimeOffset:
+      json.writeObjectFieldStart(termName);
+      json.writeStringField(DOLLAR + constExp.getExpressionName(), 
constExp.getValueAsString());
+      json.writeEndObject();
+      break;
+    case Decimal:
+      json.writeObjectFieldStart(termName);      
+      json.writeStringField(DOLLAR + constExp.getExpressionName(), 
constExp.getValueAsString());
+      json.writeEndObject();
+      break;
+    case Float:
+      json.writeObjectFieldStart(termName);
+      json.writeStringField(DOLLAR + constExp.getExpressionName(), 
constExp.getValueAsString());
+      json.writeEndObject();
+      break;
+    case Int:
+      json.writeObjectFieldStart(termName);
+      json.writeStringField(DOLLAR + constExp.getExpressionName(), 
constExp.getValueAsString());
+      json.writeEndObject();
+      break;
+    case Duration:
+      json.writeObjectFieldStart(termName);
+      json.writeStringField(DOLLAR + constExp.getExpressionName(), 
constExp.getValueAsString());
+      json.writeEndObject();
+      break;
+    case EnumMember:
+      json.writeObjectFieldStart(termName);
+      json.writeStringField(DOLLAR + constExp.getExpressionName(), 
constExp.getValueAsString());
+      json.writeEndObject();
+      break;
+    case Guid:
+      json.writeObjectFieldStart(termName);
+      json.writeStringField("$" + constExp.getExpressionName(), 
constExp.getValueAsString());
+      json.writeEndObject();
+      break;
+    case TimeOfDay:
+      json.writeObjectFieldStart(termName);
+      json.writeStringField(DOLLAR + constExp.getExpressionName(), 
constExp.getValueAsString());
+      json.writeEndObject();
+      break;
+    case Bool:
+      if (termName != null && termName.length() > 0) {
+        json.writeBooleanField(termName, 
Boolean.valueOf(constExp.getValueAsString()));
+      } else {
+        json.writeBoolean(Boolean.valueOf(constExp.getValueAsString()));
+      }
+      break;
+    case String:
+      if (termName != null && termName.length() > 0) {
+        json.writeStringField(termName, constExp.getValueAsString());
+      } else {
+        json.writeString(constExp.getValueAsString());
+      }
+      break;
+    default:
+      throw new IllegalArgumentException("Unkown ExpressionType "
+          + "for constant expression: " + constExp.getExpressionType());
+    }
+  }
+
+  private String getAliasedFullQualifiedName(final FullQualifiedName fqn) {
+    final String name;
+    if (namespaceToAlias.get(fqn.getNamespace()) != null) {
+      name = namespaceToAlias.get(fqn.getNamespace()) + "." + fqn.getName();
+    } else {
+      name = fqn.getFullQualifiedNameAsString();
+    }
+
+    return name;
+  }
+  
+  private String getFullQualifiedName(final EdmType type) {
+    return type.getFullQualifiedName().getFullQualifiedNameAsString();
+  }
+
+  private void appendReference(JsonGenerator json) throws SerializerException, 
IOException {
+    json.writeObjectFieldStart(REFERENCES);
+    for (final EdmxReference reference : serviceMetadata.getReferences()) {
+      json.writeObjectFieldStart(reference.getUri().toASCIIString());
+
+      List<EdmxReferenceInclude> includes = reference.getIncludes();
+      if (!includes.isEmpty()) {
+        appendIncludes(json, includes);
+      }
+
+      List<EdmxReferenceIncludeAnnotation> includeAnnotations = 
reference.getIncludeAnnotations();
+      if (!includeAnnotations.isEmpty()) {
+        appendIncludeAnnotations(json, includeAnnotations);
+      }
+      json.writeEndObject();
+    }
+    json.writeEndObject();
+  }
+
+  private void appendIncludeAnnotations(JsonGenerator json, 
+      List<EdmxReferenceIncludeAnnotation> includeAnnotations) throws 
SerializerException, IOException {
+    json.writeArrayFieldStart(INCLUDE_ANNOTATIONS);
+    for (EdmxReferenceIncludeAnnotation includeAnnotation : 
includeAnnotations) {
+      json.writeStartObject();
+      json.writeStringField(TERM_NAMESPACE, 
includeAnnotation.getTermNamespace());
+      if (includeAnnotation.getQualifier() != null) {
+        json.writeStringField(QUALIFIER, includeAnnotation.getQualifier());
+      }
+      if (includeAnnotation.getTargetNamespace() != null) {
+        json.writeStringField(TARGET_NAMESPACE, 
includeAnnotation.getTargetNamespace());
+      }
+      json.writeEndObject();
+    }
+    json.writeEndArray();
+  }
+
+  private void appendIncludes(JsonGenerator json, 
+      List<EdmxReferenceInclude> includes) throws SerializerException, 
IOException {
+   json.writeArrayFieldStart(INCLUDE);
+   for (EdmxReferenceInclude include : includes) {
+     json.writeStartObject();
+     json.writeStringField(NAMESPACE, include.getNamespace());
+     if (include.getAlias() != null) {
+       namespaceToAlias.put(include.getNamespace(), include.getAlias());
+       // Reference Aliases are ignored for now since they are not V2 
compatible
+       json.writeStringField(ALIAS, include.getAlias());
+     }
+     json.writeEndObject();
+   }
+   json.writeEndArray();
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/d55ed59d/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java
----------------------------------------------------------------------
diff --git 
a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java
 
b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java
index 9e4d74a..b227319 100644
--- 
a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java
+++ 
b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java
@@ -155,8 +155,25 @@ public class ODataJsonSerializer extends 
AbstractODataSerializer {
 
   @Override
   public SerializerResult metadataDocument(final ServiceMetadata 
serviceMetadata) throws SerializerException {
-    throw new SerializerException("Metadata in JSON format not supported!",
-        SerializerException.MessageKeys.JSON_METADATA);
+    OutputStream outputStream = null;
+    SerializerException cachedException = null;
+
+    try {
+      CircleStreamBuffer buffer = new CircleStreamBuffer();
+      outputStream = buffer.getOutputStream();
+      JsonGenerator json = new JsonFactory().createGenerator(outputStream);
+      new 
MetadataDocumentJsonSerializer(serviceMetadata).writeMetadataDocument(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

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/d55ed59d/lib/server-core/src/test/java/org/apache/olingo/server/core/ContentNegotiatorTest.java
----------------------------------------------------------------------
diff --git 
a/lib/server-core/src/test/java/org/apache/olingo/server/core/ContentNegotiatorTest.java
 
b/lib/server-core/src/test/java/org/apache/olingo/server/core/ContentNegotiatorTest.java
index ae6373c..f863c70 100644
--- 
a/lib/server-core/src/test/java/org/apache/olingo/server/core/ContentNegotiatorTest.java
+++ 
b/lib/server-core/src/test/java/org/apache/olingo/server/core/ContentNegotiatorTest.java
@@ -49,8 +49,10 @@ public class ContentNegotiatorTest {
   static final private String ACCEPT_CASE_MIN_IEEE754 = ACCEPT_CASE_MIN + 
";IEEE754Compatible=true";
   static final private String ACCEPT_CASE_JSONQ = "application/json;q=0.2";
   static final private String ACCEPT_CASE_XML = 
ContentType.APPLICATION_XML.toContentTypeString();
+  static final private String ACCEPT_CASE_JSON = 
ContentType.APPLICATION_JSON.toContentTypeString();
   static final private String ACCEPT_CASE_WILDCARD1 = "*/*";
   static final private String ACCEPT_CASE_WILDCARD2 = "application/*";
+  static final private String ACCEPT_CASE_JSON_IEEE754 = ACCEPT_CASE_JSON + 
";IEEE754Compatible=true";
 
   //@formatter:off (Eclipse formatter)
   //CHECKSTYLE:OFF (Maven checkstyle)
@@ -90,9 +92,25 @@ public class ContentNegotiatorTest {
       { ACCEPT_CASE_XML,        null,             ACCEPT_CASE_WILDCARD1, null  
           },
       { ACCEPT_CASE_XML,        null,             ACCEPT_CASE_WILDCARD2, null  
           },
       { "a/a",                  "a/a",            null,                  
"a/a,b/b"        },
-      { "a/a;x=y",              "a/a",            ACCEPT_CASE_WILDCARD1, 
"a/a;x=y"        }
+      { "a/a;x=y",              "a/a",            ACCEPT_CASE_WILDCARD1, 
"a/a;x=y"        },
+      { ACCEPT_CASE_JSON,       "json",           ACCEPT_CASE_JSON_IEEE754, 
null          },
+      { ACCEPT_CASE_JSON,       "json",           ACCEPT_CASE_WILDCARD1,   
null           },
+      { ACCEPT_CASE_JSON,       "application/json",ACCEPT_CASE_JSON_IEEE754, 
null         },
+      { ACCEPT_CASE_JSON_IEEE754,null,            ACCEPT_CASE_JSON_IEEE754, 
null          },
+      { ACCEPT_CASE_JSON,        null,            ACCEPT_CASE_JSON,         
null          }
   };
 
+  String[][] casesMetadataFail = {
+      /* expected               $format           accept                 
modified content types */
+      { "Unsupported $format = 
json;IEEE754Compatible=true","json;IEEE754Compatible=true", null, null},
+      { "Unsupported $format = 
json;charset=ISO-8859-1","json;charset=ISO-8859-1",     null, null},
+      { "Unsupported or illegal Accept header value: json;"
+          + "charset=ISO-8859-1 != [application/xml, application/json]",null,
+          "json;charset=ISO-8859-1", null},
+      { "Unsupported $format = application/json;charset=ISO-8859-1",
+            "application/json;charset=ISO-8859-1",null, null},
+  };
+  
   String[][] casesFail = {
       /* expected               $format           accept                 
modified content types */
       { null,                   "xxx/yyy",        null,                  null  
           },
@@ -126,9 +144,10 @@ public class ContentNegotiatorTest {
     testContentNegotiation(new String[] { ACCEPT_CASE_XML, null, null, null }, 
RepresentationType.METADATA);
   }
 
-  @Test(expected = ContentNegotiatorException.class)
-  public void metadataJsonFail() throws Exception {
-    testContentNegotiation(new String[] { null, "json", null, null }, 
RepresentationType.METADATA);
+  @Test
+  public void metadataJson() throws Exception {
+    testContentNegotiation(new String[] { ACCEPT_CASE_JSON, 
+        "application/json", null, null }, RepresentationType.METADATA);
   }
 
   @Test
@@ -149,6 +168,18 @@ public class ContentNegotiatorTest {
       }
     }
   }
+  
+  @Test
+  public void metadataFail() throws Exception {
+    for (String[] useCase : casesMetadataFail) {
+      try {
+        testContentNegotiation(useCase, RepresentationType.METADATA);
+        fail("Unsupported $format = " + useCase[1] + '|' + useCase[2] + '|' + 
useCase[3] + "'!");
+      } catch (final ContentNegotiatorException e) {
+        // Expected Exception
+      }
+    }
+  }
 
   @Test
   public void checkSupport() throws Exception {
@@ -191,10 +222,8 @@ public class ContentNegotiatorTest {
 
     final CustomContentTypeSupport customContentTypeSupport = useCase[3] == 
null ? null :
       createCustomContentTypeSupport(useCase[3]);
-
     final ContentType requestedContentType = 
ContentNegotiator.doContentNegotiation(
-        formatOption, request, customContentTypeSupport, representationType);
-
+          formatOption, request, customContentTypeSupport, representationType);
     assertNotNull(requestedContentType);
     if (useCase[0] != null) {
       assertEquals(ContentType.create(useCase[0]), requestedContentType);

Reply via email to