Example #1
0
        private static Version ParseVersion(JsonElement element, JsonParserContext context)
        {
            Debug.Assert(context != null);

            Version version    = null;
            string  strVersion = element.ParseAsString(context);

            if (context.IsSucceeded())
            {
                if (strVersion == "4.0")
                {
                    version = EdmConstants.EdmVersion4;
                }
                else if (strVersion == "4.01")
                {
                    version = EdmConstants.EdmVersion401;
                }

                // This document object MUST contain the member $Version.
                if (version == null)
                {
                    context.ReportError(EdmErrorCode.InvalidVersionNumber, Strings.CsdlJsonParser_InvalidCsdlVersion(context.Path));
                }
            }

            return(version);
        }
Example #2
0
        /// <summary>
        /// Parse the JSON number to <see cref="CsdlExpressionBase"/>.
        /// </summary>
        /// <param name="element">The JSON value to parse.</param>
        /// <param name="context">The parser context.</param>
        /// <returns>null or the parsed <see cref="CsdlExpressionBase"/>.</returns>
        private static CsdlExpressionBase ParseNumberExpression(JsonElement element, JsonParserContext context)
        {
            Debug.Assert(element.ValueKind == JsonValueKind.Number);
            Debug.Assert(context != null);

            CsdlLocation location = context.Location();

            // Int64 can handle all these integer types
            if (element.TryGetInt64(out long longValue))
            {
                return(new CsdlConstantExpression(EdmValueKind.Integer, longValue.ToString(CultureInfo.InvariantCulture), location));
            }

            // Decimal goes ahead double
            if (element.TryGetDecimal(out decimal decimalValue))
            {
                return(new CsdlConstantExpression(EdmValueKind.Decimal, decimalValue.ToString(CultureInfo.InvariantCulture), location));
            }

            // Double
            if (element.TryGetDouble(out double doubleValue))
            {
                return(new CsdlConstantExpression(EdmValueKind.Floating, doubleValue.ToString(CultureInfo.InvariantCulture), location));
            }

            // Any others?
            // Report error for unknown number
            context.ReportError(EdmErrorCode.InvalidNumberType, Strings.CsdlJsonParser_InvalidJsonNumberType(element, context.Path));
            return(null);
        }
Example #3
0
        /// <summary>
        /// Parse the IsOf expression using <see cref="JsonElement"/>.
        /// </summary>
        /// <param name="element">The input JSON element.</param>
        /// <param name="context">The parser context.</param>
        /// <param name="isOfExp">The built IsOf expression.</param>
        /// <returns>true/false</returns>
        private static bool BuildIsOfExpression(JsonElement element, JsonParserContext context, out CsdlIsTypeExpression isOfExp)
        {
            Debug.Assert(context != null);
            isOfExp = null;

            // Is-of expressions are represented as an object with a member $IsOf whose value is an annotation expression,
            JsonElement propertyValue;

            if (element.ValueKind != JsonValueKind.Object || !element.TryGetProperty("$IsOf", out propertyValue))
            {
                return(false);
            }

            // whose value is  an annotation expression,
            CsdlExpressionBase expression = propertyValue.ProcessProperty("$IsOf", context, ParseExpression);

            // a member $Type whose value is a string containing an qualified type name, and optionally a member $Collection with a value of true.

            // If the specified type is a primitive type or a collection of primitive types,
            // the facet members $MaxLength, $Unicode, $Precision, $Scale, and $SRID MAY be specified if applicable to the specified primitive type.
            // If the facet members are not specified, their values are considered unspecified.
            CsdlTypeReference typeReference = CsdlJsonParseHelper.ParseCsdlTypeReference(element, context);

            isOfExp = new CsdlIsTypeExpression(typeReference, expression, context.Location());
            return(true);
        }
Example #4
0
        /// <summary>
        /// Parse the If expression using <see cref="JsonElement"/>.
        /// </summary>
        /// <param name="element">The input JSON element.</param>
        /// <param name="context">The parser context.</param>
        /// <param name="ifExp">The built if expression.</param>
        /// <returns>true/false</returns>
        private static bool TryParseIfExpression(JsonElement element, JsonParserContext context, out CsdlIfExpression ifExp)
        {
            Debug.Assert(context != null);
            ifExp = null;

            // Conditional expressions are represented as an object with a member $If whose value is an array of two or three annotation expressions.
            JsonElement propertyValue;

            if (element.ValueKind != JsonValueKind.Object ||
                !element.TryGetProperty("$If", out propertyValue) ||
                propertyValue.ValueKind != JsonValueKind.Array)
            {
                return(false);
            }

            // first child is the test
            CsdlExpressionBase testExpression = propertyValue.ProcessItem(0, context, ParseExpression);

            // the second child is ture clause
            CsdlExpressionBase trueExpression = propertyValue.ProcessItem(1, context, ParseExpression);

            // the third child is false clause
            CsdlExpressionBase falseExpression = null;

            if (propertyValue.GetArrayLength() >= 3)
            {
                // if and only if the if-then-else expression is an item of a collection expression,
                // the third child expression MAY be omitted, reducing it to an if-then expression.
                falseExpression = propertyValue.ProcessItem(2, context, ParseExpression);
            }

            ifExp = new CsdlIfExpression(testExpression, trueExpression, falseExpression, context.Location());
            return(true);
        }
Example #5
0
        private static JsonDocument GetJsonDocument(ref Utf8JsonReader jsonReader, JsonParserContext context)
        {
            Debug.Assert(context != null);

            try
            {
                JsonDocument.TryParseValue(ref jsonReader, out JsonDocument jsonDocument);
                return(jsonDocument);
            }
            catch (JsonException jsonEx)
            {
                StringBuilder sb = new StringBuilder(context.Source != null ? context.Source : "$");
                sb.Append(" LineNumber:");
                sb.Append(jsonEx.LineNumber != null ? jsonEx.LineNumber.Value.ToString(CultureInfo.InvariantCulture) : "N/A");

                sb.Append(" BytePositionInLine:");
                sb.Append(jsonEx.BytePositionInLine != null ? jsonEx.BytePositionInLine.Value.ToString(CultureInfo.InvariantCulture) : "N/A");

                sb.Append(" Path:");
                sb.Append(jsonEx.Path != null ? jsonEx.Path : "N/A");

                sb.Append(" ActualMessage:");
                sb.Append(jsonEx.Message != null ? jsonEx.Message : "N/A");

                context.ReportError(EdmErrorCode.InvalidJson, sb.ToString());
                return(null);
            }
        }
Example #6
0
        public static CsdlTypeReference ParseCsdlTypeReference(JsonElement element, JsonParserContext context)
        {
            Debug.Assert(element.ValueKind == JsonValueKind.Object);

            // $Type
            // For single-valued terms the value of $Type is the qualified name of the property/term�s type.
            // For collection-valued terms the value of $Type is the qualified name of the term�s item type, and the member $Collection MUST be present with the literal value true.
            // Absence of the $Type member means the type is Edm.String.
            string typeName = element.ParseOptionalProperty("$Type", context, (v, p) => v.ParseAsString(p));

            typeName = typeName == null ? "Edm.String" : typeName;

            // $Collection
            // For collection-valued properties the value of $Type is the qualified name of the property�s item type,
            // and the member $Collection MUST be present with the literal value true.
            bool?collection = element.ParseOptionalProperty("$Collection", context, (v, p) => v.ParseAsBoolean(p));

            // $Nullable,
            // The value of $Nullable is one of the Boolean literals true or false. Absence of the member means false.
            bool?isNullable = element.ParseOptionalProperty("$Nullable", context, (v, p) => v.ParseAsBoolean(p));
            bool nullable   = isNullable == null ? false : isNullable.Value;

            // $MaxLength,
            // The value of $MaxLength is a positive integer.
            // CSDL xml defines a symbolic value max that is only allowed in OData 4.0 responses. This symbolic value is not allowed in CDSL JSON documents at all.
            int?maxLength = element.ParseOptionalProperty("$MaxLength", context, (v, p) => v.ParseAsInteger(p));

            // $Unicode,
            // The value of $Unicode is one of the Boolean literals true or false. Absence of the member means true.
            bool?unicode = element.ParseOptionalProperty("$Unicode", context, (v, p) => v.ParseAsBoolean(p));

            // $Precision,
            // The value of $Precision is a number.
            int?precision = element.ParseOptionalProperty("$Precision", context, (v, p) => v.ParseAsInteger(p));

            // $Scale,
            int?scale = element.ParseOptionalProperty("$Scale", context, (v, p) => v.ParseAsInteger(p));

            // $SRID,
            // The value of $SRID is a string containing a number or the symbolic value variable.
            // So far, ODL doesn't support string of SRID.
            int?srid = element.ParseOptionalProperty("$SRID", context, (v, p) => v.ParseAsInteger(p));

            CsdlLocation      location = context.Location();
            CsdlTypeReference csdlType = ParseNamedTypeReference(typeName, nullable, false, maxLength, unicode, precision, scale, srid, location);

            if (collection != null && collection.Value)
            {
                csdlType = new CsdlExpressionTypeReference(new CsdlCollectionType(csdlType, location), nullable, location);
            }

            return(csdlType);
        }
Example #7
0
        /// <summary>
        /// Parse the JSON object to <see cref="CsdlExpressionBase"/>.
        /// We don't know the object type, so try it one by one using the keyword, for example $Cast for Cast expression.
        /// </summary>
        /// <param name="element">The JSON value to parse.</param>
        /// <param name="context">The parser context.</param>
        /// <returns>null or the parsed <see cref="CsdlExpressionBase"/>.</returns>
        private static CsdlExpressionBase ParseObjectExpression(JsonElement element, JsonParserContext context)
        {
            Debug.Assert(element.ValueKind == JsonValueKind.Object);
            Debug.Assert(context != null);

            // $Path
            if (TryParseValuePathExpression(element, context, out CsdlPathExpression pathExp))
            {
                return(pathExp);
            }

            // $Cast
            if (TryParseCastExpression(element, context, out CsdlCastExpression castExp))
            {
                return(castExp);
            }

            // $Apply
            if (TryParseApplyExpression(element, context, out CsdlApplyExpression applyExp))
            {
                return(applyExp);
            }

            // $If
            if (TryParseIfExpression(element, context, out CsdlIfExpression ifExp))
            {
                return(ifExp);
            }

            // $IsOf
            if (BuildIsOfExpression(element, context, out CsdlIsTypeExpression isOfExp))
            {
                return(isOfExp);
            }

            // $LabeledElement
            if (TryParseLabeledElementExpression(element, context, out CsdlLabeledExpression labeledExp))
            {
                return(labeledExp);
            }

            // $LabeledElementReference
            if (TryParseLabeledElementReferenceExpression(element, context, out CsdlLabeledExpressionReferenceExpression labeledReferenceExp))
            {
                return(labeledReferenceExp);
            }

            // For all others, let's try build it as "Record"
            return(ParseRecordExpression(element, context));
        }
Example #8
0
        /// <summary>
        /// Parse an instance path with a Value path.
        /// An instance path is on object with a "$Path" member.
        /// "@UI.DisplayName#second": {
        ///   "$Path": "@vCard.Address#work/FullName"
        /// }
        /// </summary>
        /// <param name="element">The input JSON element.</param>
        /// <param name="context">The parser context.</param>
        /// <param name="pathExp">The built path expression.</param>
        /// <returns>true/false.</returns>
        private static bool TryParseValuePathExpression(JsonElement element, JsonParserContext context, out CsdlPathExpression pathExp)
        {
            Debug.Assert(context != null);

            pathExp = null;
            // Path expressions are represented as an object with a single member $Path
            JsonElement propertyValue;

            if (element.ValueKind != JsonValueKind.Object || !element.TryGetProperty("$Path", out propertyValue))
            {
                return(false);
            }

            // whose value is a string containing a path.
            string pathStr = propertyValue.ProcessProperty("$Path", context, (v, p) => v.ParseAsString(p));

            pathExp = new CsdlPathExpression(pathStr, context.Location());
            return(true);
        }
Example #9
0
        /// <summary>
        /// Parse the Labeled Element expression using <see cref="JsonElement"/>.
        /// </summary>
        /// <param name="element">The input JSON element.</param>
        /// <param name="context">The parser context.</param>
        /// <param name="labeledExp">The built labeled element expression.</param>
        /// <returns>true/false</returns>
        private static bool TryParseLabeledElementExpression(JsonElement element, JsonParserContext context, out CsdlLabeledExpression labeledExp)
        {
            Debug.Assert(context != null);
            labeledExp = null;

            // Labeled element expressions are represented as an object with a member $LabeledElement whose value is an annotation expression
            JsonElement propertyValue;

            if (element.ValueKind != JsonValueKind.Object || !element.TryGetProperty("$LabeledElement", out propertyValue))
            {
                return(false);
            }

            CsdlExpressionBase expression = propertyValue.ProcessProperty("$LabeledElement", context, ParseExpression);

            // a member $Name whose value is a string containing the labeled element’s name.
            string label = element.ProcessRequiredProperty("$Name", context, (v, p) => v.ParseAsString(p));

            labeledExp = new CsdlLabeledExpression(label, expression, context.Location());
            return(true);
        }
Example #10
0
        /// <summary>
        /// Build the JSON value <see cref="JsonElement"/> to collection of <see cref="IEdmReference"/>.
        /// It's weird that we parse it into IEdmReference, not CsdlReference.
        /// It is because CsdlModel needs "IEdmReference". :-(
        /// </summary>
        /// <param name="element">The input JSON element.</param>
        /// <param name="jsonPath">The parser context.</param>
        /// <returns>null or parsed collection of <see cref="IEdmReference"/>.</returns>
        internal static IList <IEdmReference> ParseReferences(JsonElement element, JsonParserContext context)
        {
            // The value of $Reference is an object that contains one member per referenced CSDL document.
            if (!element.ValidateValueKind(JsonValueKind.Object, context))
            {
                return(null);
            }

            IList <IEdmReference> references = new List <IEdmReference>();

            element.ParseAsObject(context, (url, propertyValue) =>
            {
                IEdmReference reference = ParseReference(url, propertyValue, context);
                if (reference != null)
                {
                    references.Add(reference);
                }
            });

            return(references);
        }
Example #11
0
        /// <summary>
        /// Parse the <see cref="JsonElement"/> to a <see cref="IEdmInclude"/>.
        /// </summary>
        /// <param name="element">The input JSON element.</param>
        /// <param name="context">The parser context.</param>
        /// <returns>null or parsed <see cref="IEdmInclude"/>.</returns>
        internal static CsdlInclude ParseInclude(JsonElement element, JsonParserContext context)
        {
            // Each item in $Include is an object.
            if (!element.ValidateValueKind(JsonValueKind.Object, context))
            {
                return(null);
            }

            string includeNamespace            = null;
            string includeAlias                = null;
            IList <CsdlAnnotation> annotations = new List <CsdlAnnotation>();

            element.ParseAsObject(context, (propertyName, propertyValue) =>
            {
                // Array items are objects that MUST contain the member $Namespace and MAY contain the member $Alias.
                switch (propertyName)
                {
                case "$Alias":
                    // The value of $Alias is a string containing the alias for the included schema.
                    includeAlias = propertyValue.ParseAsString(context);
                    break;

                case "$Namespace":
                    // The value of $Namespace is a string containing the namespace of the included schema
                    includeNamespace = propertyValue.ParseAsString(context);
                    break;

                default:
                    // The item objects MAY contain annotations.
                    SchemaJsonParser.ParseCsdlAnnotation(propertyName, propertyValue, context, annotations);
                    break;
                }
            });

            CsdlInclude include = new CsdlInclude(includeAlias, includeNamespace, context.Location());

            annotations.ForEach(a => include.AddAnnotation(a));
            return(include);
        }
Example #12
0
        /// <summary>
        /// Parse the Cast expression using <see cref="JsonElement"/>.
        /// </summary>
        /// <param name="element">The input JSON element.</param>
        /// <param name="context">The parser context.</param>
        /// <param name="castExp">The built cast expression.</param>
        /// <returns>true/false.</returns>
        private static bool TryParseCastExpression(JsonElement element, JsonParserContext context, out CsdlCastExpression castExp)
        {
            Debug.Assert(context != null);

            castExp = null;
            // Cast expressions are represented as an object with a member $Cast
            JsonElement propertyValue;

            if (element.ValueKind != JsonValueKind.Object || !element.TryGetProperty("$Cast", out propertyValue))
            {
                return(false);
            }

            // with a member $Cast whose value is an annotation expression
            CsdlExpressionBase expression = propertyValue.ProcessProperty("$Cast", context, ParseExpression);

            // a member $Type whose value is a string containing the qualified type name, and optionally a member $Collection with a value of true.
            CsdlTypeReference typeReference = CsdlJsonParseHelper.ParseCsdlTypeReference(element, context);

            castExp = new CsdlCastExpression(typeReference, expression, context.Location());
            return(true);
        }
Example #13
0
        /// <summary>
        /// Parse the <see cref="CsdlExpressionBase"/> from the <see cref="JsonElement"/>.
        /// </summary>
        /// <param name="element">JSON value to parse.</param>
        /// <param name="context">The parser context.</param>
        /// <returns>null or the parsed <see cref="CsdlExpressionBase"/>.</returns>
        public static CsdlExpressionBase ParseExpression(JsonElement element, JsonParserContext context)
        {
            EdmUtil.CheckArgumentNull(context, nameof(context));

            CsdlLocation location = context.Location();

            switch (element.ValueKind)
            {
            case JsonValueKind.True:
                return(new CsdlConstantExpression(EdmValueKind.Boolean, "true", location));

            case JsonValueKind.False:
                return(new CsdlConstantExpression(EdmValueKind.Boolean, "false", location));

            case JsonValueKind.String:
                // we can't distiguish "Guid, DateTimeOffset, ..." from String at here.
                // So, let's create string for all of string values.
                return(new CsdlConstantExpression(EdmValueKind.String, element.GetString(), location));

            case JsonValueKind.Null:
                return(new CsdlConstantExpression(EdmValueKind.Null, null, location));

            case JsonValueKind.Number:
                return(ParseNumberExpression(element, context));

            case JsonValueKind.Object:
                return(ParseObjectExpression(element, context));

            case JsonValueKind.Array:
                IList <CsdlExpressionBase> elements = element.ParseAsArray(context, (e, c) => ParseExpression(e, c));
                return(new CsdlCollectionExpression(null, elements, location));

            case JsonValueKind.Undefined:
            default:
                context.ReportError(EdmErrorCode.UnknownElementValueKind, Strings.CsdlJsonParser_UnknownJsonElementValueKind(element.ValueKind, context.Path));
                return(null);
            }
        }
Example #14
0
        /// <summary>
        /// Parse the <see cref="JsonElement"/> to a <see cref="IEdmInclude"/>.
        /// </summary>
        /// <param name="element">The input JSON element.</param>
        /// <param name="context">The parser context.</param>
        /// <returns>null or parsed <see cref="IEdmInclude"/>.</returns>
        internal static IEdmInclude ParseInclude(JsonElement element, JsonParserContext context)
        {
            // Each item in $Include is an object.
            if (!element.ValidateValueKind(JsonValueKind.Object, context))
            {
                return(null);
            }

            string includeNamespace = null;
            string includeAlias     = null;

            element.ParseAsObject(context, (propertyName, propertyValue) =>
            {
                // Array items are objects that MUST contain the member $Namespace and MAY contain the member $Alias.
                switch (propertyName)
                {
                case "$Alias":
                    // The value of $Alias is a string containing the alias for the included schema.
                    includeAlias = propertyValue.ParseAsString(context);
                    break;

                case "$Namespace":
                    // The value of $Namespace is a string containing the namespace of the included schema
                    includeNamespace = propertyValue.ParseAsString(context);
                    break;

                default:
                    // The item objects MAY contain annotations.
                    // However, EdmInclude does not supported yet. So skip
                    context.ReportError(EdmErrorCode.UnexpectedElement, Strings.CsdlJsonParser_UnexpectedJsonMember(context.Path, propertyValue.ValueKind));
                    break;
                }
            });

            return(new EdmInclude(includeAlias, includeNamespace));
        }
Example #15
0
        /// <summary>
        /// Parse the Apply expression using <see cref="JsonElement"/>.
        /// </summary>
        /// <param name="element">The input JSON element.</param>
        /// <param name="context">The parser context.</param>
        /// <param name="applyExp">The built apply expression.</param>
        /// <returns>true/false.</returns>
        private static bool TryParseApplyExpression(JsonElement element, JsonParserContext context, out CsdlApplyExpression applyExp)
        {
            Debug.Assert(context != null);
            applyExp = null;

            // Apply expressions are represented as an object with a member $Apply whose value is an array
            JsonElement propertyValue;

            if (element.ValueKind != JsonValueKind.Object ||
                !element.TryGetProperty("$Apply", out propertyValue) ||
                propertyValue.ValueKind != JsonValueKind.Array)
            {
                return(false);
            }

            // whose value is an array of annotation expressions,
            IList <CsdlExpressionBase> arguments = propertyValue.ProcessArrayProperty("$Apply", context, ParseExpression);

            // a member $Function whose value is a string containing the qualified name of the client-side function to be applied.
            string functionName = element.ProcessRequiredProperty("$Function", context, (e, c) => e.ParseAsString(c));

            applyExp = new CsdlApplyExpression(functionName, arguments, context.Location());
            return(true);
        }
Example #16
0
        /// <summary>
        /// Parse the <see cref="JsonElement"/> to a <see cref="IEdmReference"/>.
        /// </summary>
        /// <param name="url">The reference Url string.</param>
        /// <param name="element">The input JSON element.</param>
        /// <param name="context">The parser context.</param>
        /// <returns>null or parsed <see cref="IEdmReference"/>.</returns>
        internal static CsdlReference ParseReference(string url, JsonElement element, JsonParserContext context)
        {
            // The value of each reference object is an object.
            if (!element.ValidateValueKind(JsonValueKind.Object, context))
            {
                return(null);
            }

            IList <CsdlInclude>            includes           = null;
            IList <CsdlIncludeAnnotations> includeAnnotations = null;
            IList <CsdlAnnotation>         annotations        = new List <CsdlAnnotation>();

            element.ParseAsObject(context, (propertyName, propertyValue) =>
            {
                // The reference object MAY contain the members $Include and $IncludeAnnotations as well as annotations.
                switch (propertyName)
                {
                case "$Include":
                    // The value of $Include is an array.
                    // Array items are objects that MUST contain the member $Namespace and MAY contain the member $Alias.
                    includes = propertyValue.ParseAsArray(context, ParseInclude);
                    break;

                case "$IncludeAnnotations":
                    // The value of $IncludeAnnotations is an array.
                    // Array items are objects that MUST contain the member $TermNamespace and MAY contain the members $Qualifier and $TargetNamespace.
                    includeAnnotations = propertyValue.ParseAsArray(context, ParseIncludeAnnotations);
                    break;

                default:
                    // The reference objects MAY contain annotations.
                    SchemaJsonParser.ParseCsdlAnnotation(propertyName, propertyValue, context, annotations);
                    break;
                }
            });

            CsdlReference reference = new CsdlReference(url,
                                                        includes ?? Enumerable.Empty <CsdlInclude>(),
                                                        includeAnnotations ?? Enumerable.Empty <CsdlIncludeAnnotations>(),
                                                        context.Location());

            annotations.ForEach(a => reference.AddAnnotation(a));
            return(reference);
        }
Example #17
0
        /// <summary>
        /// Parse CSDL-JSON doc into CsdlModel, error messages are stored in <see cref="JsonParserContext"/>
        /// </summary>
        /// <param name="jsonReader">The JSON reader.</param>
        /// <param name="context">The parser context.</param>
        /// <returns>Null or parsed <see cref="CsdlModel"/>.</returns>
        internal static CsdlModel ParseCsdlDocument(ref Utf8JsonReader jsonReader, JsonParserContext context)
        {
            Debug.Assert(context != null);

            JsonDocument jsonDocument = GetJsonDocument(ref jsonReader, context);

            if (jsonDocument == null)
            {
                return(null);
            }

            // make sure to dispose the JsonDocument.
            using (jsonDocument)
            {
                JsonElement rootElement = jsonDocument.RootElement;

                // A CSDL JSON document consists of a single JSON object.
                if (!rootElement.ValidateValueKind(JsonValueKind.Object, context))
                {
                    return(null);
                }

                // This document object MUST contain the member $Version.
                Version version = rootElement.ProcessRequiredProperty("$Version", context, ParseVersion);
                if (version == null)
                {
                    return(null);
                }

                CsdlModel csdlModel = new CsdlModel
                {
                    CsdlVersion = version
                };

                IList <IEdmReference> references = null;
                rootElement.ParseAsObject(context, (propertyName, propertyValue) =>
                {
                    switch (propertyName)
                    {
                    case "$Version":
                        // skip, because processed
                        break;

                    case "$EntityContainer":
                        // The value of $EntityContainer is the namespace-qualified name of the entity container of that service.
                        // So far, i don't know how to use it. So skip it.
                        break;

                    case "$Reference":
                        // The document object MAY contain the member $Reference to reference other CSDL documents.
                        references = ParseReferences(propertyValue, context);
                        break;

                    default:
                        // CSDL document also MAY contain members for schemas.
                        // Each schema's value is an object.
                        if (propertyValue.ValueKind == JsonValueKind.Object)
                        {
                            CsdlSchema schema = SchemaJsonParser.ParseCsdlSchema(propertyName, csdlModel.CsdlVersion, propertyValue, context);
                            if (schema != null)
                            {
                                csdlModel.AddSchema(schema);
                                break;
                            }
                        }

                        context.ReportError(EdmErrorCode.UnexpectedElement, Strings.CsdlJsonParser_UnexpectedJsonMember(context.Path, propertyValue.ValueKind));
                        break;
                    }
                });


                if (references != null)
                {
                    csdlModel.AddCurrentModelReferences(references);
                }

                return(csdlModel);
            }
        }
Example #18
0
        /// <summary>
        /// Parse the <see cref="JsonElement"/> to a <see cref="IEdmIncludeAnnotations"/>.
        /// </summary>
        /// <param name="element">The input JSON element.</param>
        /// <param name="context">The parser context.</param>
        /// <returns>null or parsed <see cref="IEdmIncludeAnnotations"/>.</returns>
        internal static IEdmIncludeAnnotations ParseIncludeAnnotations(JsonElement element, JsonParserContext context)
        {
            // Each item in $IncludeAnnotations is an object.
            if (!element.ValidateValueKind(JsonValueKind.Object, context))
            {
                return(null);
            }

            string termNamespace   = null;
            string qualifier       = null;
            string targetNamespace = null;

            element.ParseAsObject(context, (propertyName, propertyValue) =>
            {
                // Array items are objects that MUST contain the member $Namespace and MAY contain the member $Alias.
                switch (propertyName)
                {
                case "$TermNamespace":
                    // The value of $TermNamespace is a namespace.
                    termNamespace = propertyValue.ParseAsString(context);
                    break;

                case "$Qualifier":
                    // The value of $Qualifier is a simple identifier.
                    qualifier = propertyValue.ParseAsString(context);
                    break;

                case "$TargetNamespace":
                    // The value of $TargetNamespace is a namespace.
                    targetNamespace = propertyValue.ParseAsString(context);
                    break;

                default:
                    // The item objects MAY contain annotations. However, IEdmIncludeAnnotations doesn't support to have annotations.
                    context.ReportError(EdmErrorCode.UnexpectedElement,
                                        Strings.CsdlJsonParser_UnexpectedJsonMember(context.Path, propertyValue.ValueKind));
                    break;
                }
            });

            return(new EdmIncludeAnnotations(termNamespace, qualifier, targetNamespace));
        }
Example #19
0
        /// <summary>
        /// Parse the <see cref="JsonElement"/> to a <see cref="IEdmReference"/>.
        /// </summary>
        /// <param name="url">The reference Url string.</param>
        /// <param name="element">The input JSON element.</param>
        /// <param name="context">The parser context.</param>
        /// <returns>null or parsed <see cref="IEdmReference"/>.</returns>
        internal static IEdmReference ParseReference(string url, JsonElement element, JsonParserContext context)
        {
            // The value of each reference object is an object.
            if (!element.ValidateValueKind(JsonValueKind.Object, context))
            {
                return(null);
            }

            IList <IEdmInclude>            includes           = null;
            IList <IEdmIncludeAnnotations> includeAnnotations = null;

            element.ParseAsObject(context, (propertyName, propertyValue) =>
            {
                // The reference object MAY contain the members $Include and $IncludeAnnotations as well as annotations.
                switch (propertyName)
                {
                case "$Include":
                    // The value of $Include is an array.
                    // Array items are objects that MUST contain the member $Namespace and MAY contain the member $Alias.
                    includes = propertyValue.ParseAsArray(context, ParseInclude);
                    break;

                case "$IncludeAnnotations":
                    // The value of $IncludeAnnotations is an array.
                    // Array items are objects that MUST contain the member $TermNamespace and MAY contain the members $Qualifier and $TargetNamespace.
                    includeAnnotations = propertyValue.ParseAsArray(context, ParseIncludeAnnotations);
                    break;

                default:
                    // The reference objects MAY contain annotations.However, EdmReference doesn't support annotation.
                    // So, skip the annotation.
                    context.ReportError(EdmErrorCode.UnexpectedElement, Strings.CsdlJsonParser_UnexpectedJsonMember(context.Path, propertyValue.ValueKind));
                    break;
                }
            });

            EdmReference edmReference = new EdmReference(new Uri(url, UriKind.RelativeOrAbsolute));

            includes.ForEach(i => edmReference.AddInclude(i));
            includeAnnotations.ForEach(i => edmReference.AddIncludeAnnotations(i));
            return(edmReference);
        }
Example #20
0
        /// <summary>
        /// Try to parse the input json value <see cref="JsonElement"/> to <see cref="CsdlAnnotation"/>.
        /// </summary>
        /// <param name="annotionName">The input annotation name, annotation name should start with '@'.</param>
        /// <param name="element">The JSON value to parse.</param>
        /// <param name="context">The parser context.</param>
        /// <param name="csdlAnnotation">The built CSDL annotation.</param>
        /// <returns>true/false.</returns>
        public static bool TryParseCsdlAnnotation(string annotionName, JsonElement element, JsonParserContext context, out CsdlAnnotation csdlAnnotation)
        {
            EdmUtil.CheckArgumentNull(context, nameof(context));

            csdlAnnotation = null;

            // An annotation is represented as a member whose name consists of an at (@) character,
            // followed by the qualified name of a term, optionally followed by a hash (#) and a qualifier.
            if (!ParseAnnotationName(annotionName, out string termName, out string qualifier))
            {
                return(false);
            }

            context.EnterScope(annotionName);

            CsdlExpressionBase expression = ParseExpression(element, context);

            context.LeaveScope(annotionName);

            csdlAnnotation = new CsdlAnnotation(termName, qualifier, expression, context.Location());
            return(true);
        }
Example #21
0
        /// <summary>
        /// Parse the Labeled Element Reference expression using <see cref="JsonElement"/>.
        /// </summary>
        /// <param name="element">The input JSON element.</param>
        /// <param name="context">The parser context.</param>
        /// <param name="labeledReferenceExp">The built Labeled Element Reference expression.</param>
        /// <returns>true/false</returns>
        private static bool TryParseLabeledElementReferenceExpression(JsonElement element, JsonParserContext context, out CsdlLabeledExpressionReferenceExpression labeledReferenceExp)
        {
            Debug.Assert(context != null);
            labeledReferenceExp = null;

            // Labeled element reference expressions are represented as an object with a member $LabeledElementReference whose value is a string containing an qualified name.
            JsonElement propertyValue;

            if (element.ValueKind != JsonValueKind.Object || !element.TryGetProperty("$LabeledElementReference", out propertyValue))
            {
                return(false);
            }

            string label = propertyValue.ProcessProperty("$LabeledElementReference", context, (e, c) => e.ParseAsString(c));

            labeledReferenceExp = new CsdlLabeledExpressionReferenceExpression(label, context.Location());
            return(true);
        }
Example #22
0
        /// <summary>
        /// Parse the record expression expression using <see cref="JsonElement"/>.
        /// </summary>
        /// <param name="element">The input JSON element.</param>
        /// <param name="context">The parser context.</param>
        /// <returns>the built record expression.</returns>
        private static CsdlRecordExpression ParseRecordExpression(JsonElement element, JsonParserContext context)
        {
            Debug.Assert(context != null);

            // A record expression MAY specify the structured type of its result, which MUST be an entity type or complex type in scope.
            // If not explicitly specified, the type is derived from the expression’s context

            // The type of a record expression is represented as the @type control information
            // for example: "@type": "https://example.org/vocabs/person#org.example.person.Manager",
            // So far, ODL doesn't support the type "@type" with relative path, only supports like "#Model.VipCustomer", or without #
            // for 4.0, this name MUST be prefixed with the hash symbol (#);
            // or non-OData 4.0 payloads, built-in primitive type values SHOULD be represented without the hash symbol,
            // but consumers of 4.01 or greater payloads MUST support values with or without the hash symbol.

            CsdlTypeReference typeReference = null;

            if (element.TryGetProperty("@type", out JsonElement typeValue))
            {
                // Try to build the type. The type should be "Complex type" or "Entity Type".
                string typeName = typeValue.ProcessProperty("@type", context, (e, c) => e.ParseAsString(c));
                int    index    = typeName.IndexOf('#');
                if (index >= 0)
                {
                    typeName = typeName.Substring(index + 1); // remove the "#"
                }

                typeReference = new CsdlNamedTypeReference(typeName, true, context.Location());
            }

            IList <CsdlPropertyValue> propertyValues = new List <CsdlPropertyValue>();

            element.ParseAsObject(context, (propertyName, propertyValue) =>
            {
                // skips the @type, because it's processed above.
                if (propertyName == "@type")
                {
                    return;
                }

                // It MAY contain annotations for itself and its members. Annotations for record members are prefixed with the member name.
                // So far, it's not supported. So report non-fatal error for all the annotations on record.
                if (propertyName.IndexOf('@') != -1)
                {
                    context.ReportError(EdmErrorCode.UnsupportedElement, Strings.CsdlJsonParser_UnsupportedJsonMember(context.Path));
                    return;
                }

                CsdlExpressionBase propertyValueExpression = ParseExpression(propertyValue, context);
                propertyValues.Add(new CsdlPropertyValue(propertyName, propertyValueExpression, context.Location()));
            });

            return(new CsdlRecordExpression(typeReference, propertyValues, context.Location()));
        }