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); }
/// <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); }
/// <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); }
/// <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); }
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); } }
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); }
/// <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)); }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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); } }
/// <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)); }
/// <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); }
/// <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); }
/// <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); } }
/// <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)); }
/// <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); }
/// <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); }
/// <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); }
/// <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())); }