/// <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.")));
            }
        }
Пример #6
0
        /// <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.")));
            }
        }
Пример #8
0
        /// <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.")));
            }
        }
Пример #9
0
        /// <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.")));
            }
        }