/// <summary> /// Reads an <see cref="MapSchema" />. /// </summary> /// <returns> /// A successful <see cref="JsonSchemaReaderCaseResult" /> with an <see cref="MapSchema" /> /// if <paramref name="element" /> is an map schema; an unsuccessful /// <see cref="JsonSchemaReaderCaseResult" /> with an <see cref="UnknownSchemaException" /> /// otherwise. /// </returns> /// <exception cref="InvalidSchemaException"> /// Thrown when an values property is not present on the schema. /// </exception> /// <inheritdoc /> public virtual JsonSchemaReaderCaseResult Read(JsonElement element, JsonSchemaReaderContext context) { if (element.ValueKind == JsonValueKind.Object && element.TryGetProperty(JsonAttributeToken.Type, out var type) && type.ValueEquals(JsonSchemaToken.Map)) { if (!element.TryGetProperty(JsonAttributeToken.Values, out var values)) { throw new InvalidSchemaException($"\"{JsonSchemaToken.Map}\" schemas must contain an \"{JsonAttributeToken.Values}\" key."); } var child = Reader.Read(values, context); var key = $"{JsonSchemaToken.Map}<{context.Schemas.Single(p => p.Value == child).Key}>"; if (!context.Schemas.TryGetValue(key, out var schema)) { schema = new MapSchema(child); context.Schemas.Add(key, schema); } return(JsonSchemaReaderCaseResult.FromSchema(schema)); } else { return(JsonSchemaReaderCaseResult.FromException(new UnknownSchemaException($"{nameof(JsonMapSchemaReaderCase)} can only be applied to \"{JsonSchemaToken.Map}\" schemas."))); } }
/// <summary> /// Reads a <see cref="LongSchema" /> with a <see cref="MicrosecondTimestampLogicalType" />. /// </summary> /// <returns> /// A successful <see cref="JsonSchemaReaderCaseResult" /> with an <see cref="LongSchema" /> /// if <paramref name="element" /> is a long schema with a microsecond timestamp logical type; /// an unsuccessful <see cref="JsonSchemaReaderCaseResult" /> with an <see cref="UnknownSchemaException" /> /// otherwise. /// </returns> /// <inheritdoc /> public virtual JsonSchemaReaderCaseResult Read(JsonElement element, JsonSchemaReaderContext context) { if (element.ValueKind == JsonValueKind.Object && element.TryGetProperty(JsonAttributeToken.Type, out var type) && type.ValueEquals(JsonSchemaToken.Long) && element.TryGetProperty(JsonAttributeToken.LogicalType, out var logicalType) && logicalType.ValueEquals(JsonSchemaToken.TimestampMicroseconds)) { var key = $"{JsonSchemaToken.Long}!{JsonSchemaToken.TimestampMicroseconds}"; if (!context.Schemas.TryGetValue(key, out var schema)) { schema = new LongSchema() { LogicalType = new MicrosecondTimestampLogicalType(), }; context.Schemas.Add(key, schema); } return(JsonSchemaReaderCaseResult.FromSchema(schema)); } else { return(JsonSchemaReaderCaseResult.FromException(new UnknownSchemaException($"{nameof(JsonMicrosecondTimestampSchemaReaderCase)} can only be applied to \"{JsonSchemaToken.Long}\" schemas with the \"{JsonSchemaToken.TimestampMicroseconds}\" logical type."))); } }
/// <summary> /// Reads a <see cref="BytesSchema" /> or <see cref="FixedSchema" /> with a /// <see cref="DecimalLogicalType" />. /// </summary> /// <returns> /// A successful <see cref="JsonSchemaReaderCaseResult" /> with a <see cref="BytesSchema" /> /// or <see cref="FixedSchema" /> if <paramref name="element" /> is a bytes or fixed schema /// with a decimal logical type; an unsuccessful <see cref="JsonSchemaReaderCaseResult" /> /// with an <see cref="UnknownSchemaException" /> otherwise. /// </returns> /// <exception cref="InvalidSchemaException"> /// Thrown when precision or scale properties are not present on the schema. /// </exception> /// <inheritdoc /> public virtual JsonSchemaReaderCaseResult Read(JsonElement element, JsonSchemaReaderContext context) { if (element.ValueKind == JsonValueKind.Object && element.TryGetProperty(JsonAttributeToken.Type, out var type) && element.TryGetProperty(JsonAttributeToken.LogicalType, out var logicalType) && logicalType.ValueEquals(JsonSchemaToken.Decimal)) { if (!element.TryGetProperty(JsonAttributeToken.Precision, out var precision) || precision.ValueKind != JsonValueKind.Number) { throw new InvalidSchemaException($"Schemas with the \"{JsonSchemaToken.Decimal}\" logical type must contain a \"{JsonAttributeToken.Precision}\" key."); } if (element.TryGetProperty(JsonAttributeToken.Scale, out var scale) && scale.ValueKind != JsonValueKind.Number) { throw new InvalidSchemaException($"Schemas with the \"{JsonSchemaToken.Decimal}\" logical type must contain a \"{JsonAttributeToken.Scale}\" key."); } if (type.ValueEquals(JsonSchemaToken.Bytes)) { var key = $"{JsonSchemaToken.Bytes}!{JsonSchemaToken.Decimal}!{precision.GetInt32()}!{(scale.Equals(default) ? 0 : scale.GetInt32())}";
/// <summary> /// Reads a <see cref="PrimitiveSchema " />. /// </summary> /// <returns> /// A successful <see cref="JsonSchemaReaderCaseResult" /> with a <see cref="PrimitiveSchema" /> /// if <paramref name="element" /> is a primitive schema; an unsuccessful /// <see cref="JsonSchemaReaderCaseResult" /> with an <see cref="UnknownSchemaException" /> /// otherwise. /// </returns> /// <inheritdoc /> public virtual JsonSchemaReaderCaseResult Read(JsonElement element, JsonSchemaReaderContext context) { if (element.ValueKind == JsonValueKind.String) { var name = element.GetString(); var qualifiedName = QualifyName(name, context.Scope); if (context.Schemas.TryGetValue(qualifiedName, out var schema)) { return(JsonSchemaReaderCaseResult.FromSchema(schema)); } if (name != qualifiedName && context.Schemas.TryGetValue(name, out schema)) { return(JsonSchemaReaderCaseResult.FromSchema(schema)); } return(JsonSchemaReaderCaseResult.FromException(new UnknownSchemaException($"\"{name}\" is not a known schema."))); } return(JsonSchemaReaderCaseResult.FromException(new UnknownSchemaException($"{nameof(JsonNamedSchemaReaderCase)} can only be applied to named schema references."))); }
/// <summary> /// Reads a <see cref="UnionSchema" />. /// </summary> /// <returns> /// A successful <see cref="JsonSchemaReaderCaseResult" /> with a <see cref="UnionSchema" /> /// if <paramref name="element" /> is a union schema; an unsuccessful /// <see cref="JsonSchemaReaderCaseResult" /> with an <see cref="UnknownSchemaException" /> /// otherwise. /// </returns> /// <inheritdoc /> public virtual JsonSchemaReaderCaseResult Read(JsonElement element, JsonSchemaReaderContext context) { if (element.ValueKind == JsonValueKind.Array) { var children = element .EnumerateArray() .Select(child => Reader.Read(child, context)) .ToArray(); var key = $"[{string.Join(",", children.Select(s => context.Schemas.Single(p => p.Value == s).Key))}]"; if (!context.Schemas.TryGetValue(key, out var schema)) { schema = new UnionSchema(children); context.Schemas.Add(key, schema); } return(JsonSchemaReaderCaseResult.FromSchema(schema)); } else { return(JsonSchemaReaderCaseResult.FromException(new UnknownSchemaException($"{nameof(JsonUnionSchemaReaderCase)} can only be applied to union schemas."))); } }
/// <summary> /// Reads a <see cref="PrimitiveSchema " />. /// </summary> /// <returns> /// A successful <see cref="JsonSchemaReaderCaseResult" /> with a <see cref="PrimitiveSchema" /> /// if <paramref name="element" /> is a primitive schema; an unsuccessful /// <see cref="JsonSchemaReaderCaseResult" /> with an <see cref="UnknownSchemaException" /> /// otherwise. /// </returns> /// <inheritdoc /> public virtual JsonSchemaReaderCaseResult Read(JsonElement element, JsonSchemaReaderContext context) { if (element.ValueKind == JsonValueKind.Object) { element.TryGetProperty(JsonAttributeToken.Type, out element); } if (element.ValueKind == JsonValueKind.String) { var key = element.GetString(); if (!context.Schemas.TryGetValue(key, out var schema)) { schema = key switch { JsonSchemaToken.Boolean => new BooleanSchema(), JsonSchemaToken.Bytes => new BytesSchema(), JsonSchemaToken.Double => new DoubleSchema(), JsonSchemaToken.Float => new FloatSchema(), JsonSchemaToken.Int => new IntSchema(), JsonSchemaToken.Long => new LongSchema(), JsonSchemaToken.Null => new NullSchema(), JsonSchemaToken.String => new StringSchema(), _ => default,
/// <summary> /// Reads a <see cref="FixedSchema" />. /// </summary> /// <returns> /// A successful <see cref="JsonSchemaReaderCaseResult" /> with a <see cref="FixedSchema" /> /// if <paramref name="element" /> is a fixed schema an unsuccessful /// <see cref="JsonSchemaReaderCaseResult" /> with an <see cref="UnknownSchemaException" /> /// otherwise. /// </returns> /// <exception cref="InvalidSchemaException"> /// Thrown when the size property is not present on the schema. /// </exception> /// <inheritdoc /> public virtual JsonSchemaReaderCaseResult Read(JsonElement element, JsonSchemaReaderContext context) { if (element.ValueKind == JsonValueKind.Object && element.TryGetProperty(JsonAttributeToken.Type, out var type) && type.ValueEquals(JsonSchemaToken.Fixed)) { if (!element.TryGetProperty(JsonAttributeToken.Name, out var name) || name.ValueKind != JsonValueKind.String) { throw new InvalidSchemaException($"Named schemas must contain a \"{JsonAttributeToken.Name}\" key."); } if (!element.TryGetProperty(JsonAttributeToken.Size, out var size) || size.ValueKind != JsonValueKind.Number) { throw new InvalidSchemaException($"\"{JsonSchemaToken.Fixed}\" schemas must contain a \"{JsonAttributeToken.Size}\" key."); } var scope = element.TryGetProperty(JsonAttributeToken.Namespace, out var @namespace) ? @namespace.GetString() : context.Scope; var schema = new FixedSchema(QualifyName(name.GetString(), scope), size.GetInt32()); if (element.TryGetProperty(JsonAttributeToken.Aliases, out var aliases)) { schema.Aliases = aliases.EnumerateArray() .Select(alias => QualifyName(alias.GetString(), scope)) .ToArray(); } try { context.Schemas.Add(schema.FullName, schema); } catch (ArgumentException) { throw new InvalidSchemaException($"Invalid name; a definition for {schema.FullName} was already read."); } foreach (var alias in schema.Aliases) { if (alias == schema.FullName) { continue; } try { context.Schemas.Add(alias, schema); } catch (ArgumentException) { throw new InvalidSchemaException($"Invalid alias; a definition for {alias} was already read."); } } return(JsonSchemaReaderCaseResult.FromSchema(schema)); } else { return(JsonSchemaReaderCaseResult.FromException(new UnknownSchemaException($"{nameof(JsonFixedSchemaReaderCase)} can only be applied to \"{JsonSchemaToken.Fixed}\" schemas."))); } }
/// <summary> /// Reads a <see cref="RecordSchema" />. /// </summary> /// <returns> /// A successful <see cref="JsonSchemaReaderCaseResult" /> with a <see cref="RecordSchema" /> /// if <paramref name="element" /> is a record schema; an unsuccessful /// <see cref="JsonSchemaReaderCaseResult" /> with an <see cref="UnknownSchemaException" /> /// otherwise. /// </returns> /// <exception cref="InvalidSchemaException"> /// Thrown when a fields property is not present on the schema. /// </exception> /// <inheritdoc /> public virtual JsonSchemaReaderCaseResult Read(JsonElement element, JsonSchemaReaderContext context) { if (element.ValueKind == JsonValueKind.Object && element.TryGetProperty(JsonAttributeToken.Type, out var type) && type.ValueEquals(JsonSchemaToken.Record)) { if (!element.TryGetProperty(JsonAttributeToken.Name, out var name) || name.ValueKind != JsonValueKind.String) { throw new InvalidSchemaException($"Named schemas must contain a \"{JsonAttributeToken.Name}\" key."); } if (!element.TryGetProperty(JsonAttributeToken.Fields, out var fields)) { throw new InvalidSchemaException($"\"{JsonSchemaToken.Record}\" schemas must contain a \"{JsonAttributeToken.Fields}\" key."); } var scope = element.TryGetProperty(JsonAttributeToken.Namespace, out var @namespace) ? @namespace.GetString() : context.Scope; var schema = new RecordSchema(QualifyName(name.GetString(), scope)); if (element.TryGetProperty(JsonAttributeToken.Aliases, out var aliases)) { schema.Aliases = aliases.EnumerateArray() .Select(alias => QualifyName(alias.GetString(), scope)) .ToArray(); } if (element.TryGetProperty(JsonAttributeToken.Doc, out var doc)) { schema.Documentation = doc.GetString(); } try { context.Schemas.Add(schema.FullName, schema); } catch (ArgumentException) { throw new InvalidSchemaException($"Invalid name; a definition for {schema.FullName} was already read."); } foreach (var alias in schema.Aliases) { if (alias == schema.FullName) { continue; } try { context.Schemas.Add(alias, schema); } catch (ArgumentException) { throw new InvalidSchemaException($"Invalid alias; a definition for {alias} was already read."); } } var originalScope = context.Scope; context.Scope = scope; foreach (JsonElement fieldElement in fields.EnumerateArray()) { if (!fieldElement.TryGetProperty(JsonAttributeToken.Name, out var fieldName) || fieldName.ValueKind != JsonValueKind.String) { throw new InvalidSchemaException($"Record fields must contain a \"{JsonAttributeToken.Name}\" key."); } if (!fieldElement.TryGetProperty(JsonAttributeToken.Type, out var fieldType)) { throw new InvalidSchemaException($"Record fields must contain a \"{JsonAttributeToken.Type}\" key."); } var field = new RecordField(fieldName.GetString(), Reader.Read(fieldType, context)); if (fieldElement.TryGetProperty(JsonAttributeToken.Default, out var fieldDefault)) { field.Default = new JsonDefaultValue(fieldDefault, field.Type, DeserializerBuilder); } if (fieldElement.TryGetProperty(JsonAttributeToken.Doc, out var fieldDoc)) { field.Documentation = fieldDoc.GetString(); } schema.Fields.Add(field); } context.Scope = originalScope; return(JsonSchemaReaderCaseResult.FromSchema(schema)); } else { return(JsonSchemaReaderCaseResult.FromException(new UnknownSchemaException($"{nameof(JsonRecordSchemaReaderCase)} can only be applied to \"{JsonSchemaToken.Record}\" schemas."))); } }
/// <summary> /// Reads an <see cref="EnumSchema" />. /// </summary> /// <returns> /// A successful <see cref="JsonSchemaReaderCaseResult" /> with a <see cref="BytesSchema" /> /// or <see cref="FixedSchema" /> if <paramref name="element" /> is a bytes or fixed schema /// with a decimal logical type; an unsuccessful <see cref="JsonSchemaReaderCaseResult" /> /// with an <see cref="UnknownSchemaException" /> otherwise. /// </returns> /// <exception cref="InvalidSchemaException"> /// Thrown when a symbols property is not present on the schema. /// </exception> /// <inheritdoc /> public virtual JsonSchemaReaderCaseResult Read(JsonElement element, JsonSchemaReaderContext context) { if (element.ValueKind == JsonValueKind.Object && element.TryGetProperty(JsonAttributeToken.Type, out var type) && type.ValueEquals(JsonSchemaToken.Enum)) { if (!element.TryGetProperty(JsonAttributeToken.Name, out var name) || name.ValueKind != JsonValueKind.String) { throw new InvalidSchemaException($"Named schemas must contain a \"{JsonAttributeToken.Name}\" key."); } if (!element.TryGetProperty(JsonAttributeToken.Symbols, out var symbols) || symbols.ValueKind != JsonValueKind.Array) { throw new InvalidSchemaException($"\"{JsonSchemaToken.Enum}\" schemas must contain a \"{JsonAttributeToken.Symbols}\" key."); } var scope = element.TryGetProperty(JsonAttributeToken.Namespace, out var @namespace) ? @namespace.GetString() : context.Scope; var schema = new EnumSchema(QualifyName(name.GetString(), scope)) { Symbols = symbols.EnumerateArray().Select(symbol => symbol.GetString()).ToArray(), }; if (element.TryGetProperty(JsonAttributeToken.Aliases, out var aliases)) { schema.Aliases = aliases.EnumerateArray() .Select(alias => QualifyName(alias.GetString(), scope)) .ToArray(); } if (element.TryGetProperty(JsonAttributeToken.Default, out var @default)) { schema.Default = @default.GetString(); if (!schema.Symbols.Contains(schema.Default)) { throw new InvalidSchemaException($"The default value \"{schema.Default}\" is not a symbol in {schema.FullName}."); } } if (element.TryGetProperty(JsonAttributeToken.Doc, out var doc)) { schema.Documentation = doc.GetString(); } try { context.Schemas.Add(schema.FullName, schema); } catch (ArgumentException) { throw new InvalidSchemaException($"Invalid name; a definition for {schema.FullName} was already read."); } foreach (var alias in schema.Aliases) { if (alias == schema.FullName) { continue; } try { context.Schemas.Add(alias, schema); } catch (ArgumentException) { throw new InvalidSchemaException($"Invalid alias; a definition for {alias} was already read."); } } return(JsonSchemaReaderCaseResult.FromSchema(schema)); } else { return(JsonSchemaReaderCaseResult.FromException(new UnknownSchemaException($"{nameof(JsonEnumSchemaReaderCase)} can only be applied to \"{JsonSchemaToken.Enum}\" schemas."))); } }