Exemple #1
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()));
        }
Exemple #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);
        }
Exemple #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);
        }
Exemple #4
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);
        }
Exemple #5
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);
        }
Exemple #6
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);
        }
Exemple #7
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);
        }
Exemple #8
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);
        }
Exemple #9
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);
        }
Exemple #10
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);
        }
Exemple #11
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);
            }
        }
Exemple #12
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);
        }
Exemple #13
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 CsdlIncludeAnnotations 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 doesn't contain vocabulary annotation.
                    context.ReportError(EdmErrorCode.UnexpectedElement,
                                        Strings.CsdlJsonParser_UnexpectedJsonMember(context.Path, propertyValue.ValueKind));
                    break;
                }
            });

            return(new CsdlIncludeAnnotations(termNamespace, qualifier, targetNamespace, context.Location()));
        }
Exemple #14
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);
        }
Exemple #15
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);
        }