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