Ejemplo en C# de metaesquema v1

Ejemplo de AC# para validar cargas útiles con instancias del metaesquema de definición de tipo de producto de Amazon.

Ejemplo de implementación del validador para .NET

Para aplicaciones C# .NET, la biblioteca de esquemas Newtonsoft Json.NET es compatible con JSON Schema Draft 2019-09 y vocabularios personalizados. El siguiente ejemplo demuestra cómo utilizar la biblioteca de esquemas Json.NET de Newtonsoft para validar cargas útiles con instancias del metaesquema de definición de tipo de producto de Amazon . No hay ningún requisito para usar esta biblioteca específica o la implementación de ejemplo. Amazon no proporciona soporte técnico para bibliotecas de esquemas JSON de terceros y esto se proporciona solo como ejemplo.

Configuración del esquema

Cuando se utiliza el esquema Json.NET de Newtonsoft para validar instancias del metaesquema de definición de tipo de producto de Amazon con vocabulario personalizado, el metaesquema se configura con un JSchemaResolver y la validación de keywords (palabras clave) personalizadas se configuran con JSchemaReaderSettings al analizar instancias del metaesquema de definición de tipo de producto de Amazon .

constantes :

// $id of the Amazon Product Type Definition Meta-Schema.
var schemaId = "https://schemas.amazon.com/selling-partners/definitions/product-types/meta-schema/v1";

// Local copy of the Amazon Product Type Definition Meta-Schema.
var metaSchemaPath = "./amazon-product-type-definition-meta-schema-v1.json";

// Local copy of an instance of the Amazon Product Type Definition Meta-Schema.
var luggageSchemaPath = "./luggage.json";

Configurar Meta-Esquema :

// Schema resolver that uses local copies of schemas rather than retrieving them from the web.
var resolver = new JSchemaPreloadedResolver();

// Add the meta-schema to the resolver.
resolver.Add(new Uri(schemaId), File.ReadAllText(metaSchemaPath));

// Configure reader settings with resolver and keyword validators to use when parsing instances of the meta-schema.
var readerSettings = new JSchemaReaderSettings
    {
        Resolver = resolver,
        Validators = new List<JsonValidator>
        {
            new MaxUniqueItemsKeywordValidator(),
            new MaxUtf8ByteLengthKeywordValidator(),
            new MinUtf8ByteLengthKeywordValidator()
        }
    };

Cargar instancia de metaesquema :

var luggageSchema = JSchema.Parse(File.ReadAllText(luggageSchemaPath), readerSettings);

Validación de carga útil

Con una instancia del metaesquema de definición de tipo de producto de Amazon cargado como Instancia de JSchema , las cargas útiles se pueden validar utilizando la instancia.

// Create a JObject for the payload (this can be constructed in code, read from a file, etc.).
var payload = JObject.Parse(File.ReadAllText("./payload.json"));

// Validate the payload and get any resulting validation messages.
var valid = payload.IsValid(luggageSchema, out IList<string> errorMessages);

Si la carga útil es válida, IsValid will return true con una lista vacía de mensajes de error. De lo contrario IsValid volverá false con una lista de mensajes de error.

Validación de keywords (palabras clave)

El esquema Newtonsoft Json.NET admite la validación de vocabulario personalizado mediante el uso de clases que implementan el interfaz JsonValidator y proporciona la lógica de validación.

Consulte https://www.newtonsoft.com/jsonschema/help/html/CustomJsonValidators.htm .

Los siguientes ejemplos ilustran implementaciones del Interfaz JsonValidator que valida el vocabulario personalizado en instancias del metaesquema de definición de tipo de producto de Amazon .

MaxUniqueItemsClase de keywords (palabras clave)

using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Schema;

namespace AmazonProductTypeSchemaValidator
{
    /**
     * Example validator for the "maxUniqueItems" keyword.
     */
    public class MaxUniqueItemsKeywordValidator : JsonValidator
    {
        public override void Validate(JToken value, JsonValidatorContext context)
        {
            var maxUniqueItems = GetMaxUniqueItems(context.Schema);
            
            // Get the selector properties configured on the scheme element, if they exist. Otherwise, this validator
            // defaults to using all properties.
            var selectors = GetSelectors(context.Schema);

            // Only process if the value is an array with values.
            if (value.Type != JTokenType.Array) return;
            
            // Create a property-value dictionary of each items properties (selectors) and count the number of
            // occurrences for each combination.
            var uniqueItemCounts = new Dictionary<IDictionary<string, string>, int>(new UniqueKeyComparer());
            foreach (var instance in value)
            {
                // Only process instances in the array that are objects.
                if (instance.Type != JTokenType.Object) continue;

                var instanceObject = JObject.FromObject(instance);
                var uniqueKeys = instanceObject.Properties()
                    .Where(property => selectors.Count == 0 || selectors.Contains(property.Name))
                    .ToDictionary(property => property.Name, property => property.Value.ToString());

                var count = uniqueItemCounts.GetValueOrDefault(uniqueKeys, 0) + 1;
                uniqueItemCounts[uniqueKeys] = count;
            }
            
            // Find first selector combination with too many instances.
            var (uniqueKey, itemCount) = uniqueItemCounts.FirstOrDefault(entry => entry.Value > maxUniqueItems);
            if (itemCount > 0)
            {
                var selectorValues = string.Join(", ", uniqueKey.Select(keyValuePair => $"{keyValuePair.Key}={keyValuePair.Value}").ToList());
                context.RaiseError($"Each combination of selector values may only occur {maxUniqueItems} times. " +
                                   $"The following selector value combination occurs too many times: {{{selectorValues}}}");
            }
        }

        public override bool CanValidate(JSchema schema)
        {
            return GetMaxUniqueItems(schema) >= 0;
        }

        private static IList<string> GetSelectors(JSchema schema)
        {
            var selectors = new List<string>();
            
            var schemaObject = JObject.FromObject(schema);
            var selectorsProperty = schemaObject["selectors"];

            if (selectorsProperty.HasValues)
                selectors.AddRange(selectorsProperty.Select(selector => selector.ToString()));

            return selectors;
        }

        private static int GetMaxUniqueItems(JSchema schema)
        {
            var schemaObject = JObject.FromObject(schema);
            var maxUniqueItemsProperty = schemaObject["maxUniqueItems"];

            if (maxUniqueItemsProperty != null && int.TryParse(maxUniqueItemsProperty.ToString(), out var maxUniqueItems))
                return maxUniqueItems;

            return -1;
        }

        /**
         * "Deep" comparator for unique keys dictionary to enable use as a dictionary key.
         */
        private class UniqueKeyComparer : IEqualityComparer<IDictionary<string, string>>
        {
            public bool Equals(IDictionary<string, string> x, IDictionary<string, string> y)
            {
                return x.Count == y.Count 
                       && x.Aggregate(true, (current, keyValuePair) => current && keyValuePair.Value == y[keyValuePair.Key]);
            }

            public int GetHashCode(IDictionary<string, string> obj)
            {
                return ("Keys_" + string.Join(",", obj.Select(o => o.Key))
                        + "_Values_" + string.Join(",", obj.Select(o => o.Value))).GetHashCode();
            }
        }
    }
}

MaxUtf8ByteLengthClase de keyword

using System.Text;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Schema;

namespace AmazonProductTypeSchemaValidator
{
    /**
     * Example validator for the "maxUtf8ByteLength" keyword.
     */
    public class MaxUtf8ByteLengthKeywordValidator : JsonValidator
    {
        public override void Validate(JToken value, JsonValidatorContext context)
        {
            var maxUtf8ByteLength = GetMaxUtf8ByteLength(context.Schema);
            if (Encoding.UTF8.GetBytes(value.ToString()).Length > maxUtf8ByteLength)
                context.RaiseError($"Value must be less than or equal {maxUtf8ByteLength} bytes in length.");
        }

        public override bool CanValidate(JSchema schema)
        {
            return GetMaxUtf8ByteLength(schema) >= 0;
        }

        private static int GetMaxUtf8ByteLength(JSchema schema)
        {
            var schemaObject = JObject.FromObject(schema);
            var byteLengthProperty = schemaObject["maxUtf8ByteLength"];

            if (byteLengthProperty != null && int.TryParse(byteLengthProperty.ToString(), out var maxUtf8ByteLength))
                return maxUtf8ByteLength;

            return -1;
        }
    }
}

MinUtf8ByteLengthClase de keyword

using System.Text;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Schema;

namespace AmazonProductTypeSchemaValidator
{
    /**
     * Example validator for the "minUtf8ByteLength" keyword.
     */
    public class MinUtf8ByteLengthKeywordValidator : JsonValidator
    {
        public override void Validate(JToken value, JsonValidatorContext context)
        {
            var minUtf8ByteLength = GetMinUtf8ByteLength(context.Schema);
            if (Encoding.UTF8.GetBytes(value.ToString()).Length < minUtf8ByteLength)
                context.RaiseError($"Value must be greater than or equal {minUtf8ByteLength} bytes in length.");
        }

        public override bool CanValidate(JSchema schema)
        {
            return GetMinUtf8ByteLength(schema) >= 0;
        }

        private static int GetMinUtf8ByteLength(JSchema schema)
        {
            var schemaObject = JObject.FromObject(schema);
            var byteLengthProperty = schemaObject["minUtf8ByteLength"];

            if (byteLengthProperty != null && int.TryParse(byteLengthProperty.ToString(), out var minUtf8ByteLength))
                return minUtf8ByteLength;

            return -1;
        }
    }
}