Exemplo em C# de Meta-Schema (v1)
Um exemplo em C# para validar payloads com instâncias do Meta-Schema de definição de tipo de produto da Amazon.
Exemplo de implementação do validador para .NET
Para aplicativos C# .NET, a biblioteca Newtonsoft Json.NET Schema é compatível com JSON Schema Draft 2019-09 e vocabulários personalizados. O exemplo a seguir demonstra como utilizar a biblioteca Newtonsoft Json.NET Schema para validar cargas com instâncias do Meta-Schema de definição de tipo de produto da Amazon. Não há necessidade de usar essa biblioteca específica ou a implementação de exemplo. A Amazon não fornece suporte técnico para bibliotecas de esquema JSON de terceiros, isso é fornecido apenas como exemplo.
Configuração do Schema
Ao usar o Newtonsoft Json.NET Schema para validar instâncias do Meta-Schema de definição de tipo de produto da Amazon com vocabulário personalizado, o Meta-Schema é configurado com um JSchemaResolver
e a validação de palavra-chave personalizada é configurada com JSchemaReaderSettings
ao analisar instâncias do Meta-Schema de definição de tipo de produto da 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-Schema:
// 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()
}
};
Carregar instância do Meta-Schema:
var luggageSchema = JSchema.Parse(File.ReadAllText(luggageSchemaPath), readerSettings);
Validação do Payload
Com uma instância do Meta-Schema de definição de tipo de produto da Amazon carregada como uma instância JSchema
, as cargas úteis podem ser validadas usando a instância.
// 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);
Se a carga útil for válida, IsValid
retornará true
com uma lista vazia de mensagens de erro. Caso contrário, IsValid
retornará false
com uma lista de mensagens de erro.
Validação de palavra-chave
O Newtonsoft Json.NET Schema dá suporte à validação de vocabulário personalizado usando classes que implementam a interface JsonValidator
e fornecem a lógica de validação.
Consulte https://www.newtonsoft.com/jsonschema/help/html/CustomJsonValidators.htm.
Os exemplos a seguir ilustram as implementações da interface JsonValidator
que validam o vocabulário personalizado em instâncias do Meta-Schema de definição de tipo de produto da Amazon.
Classe MaxUniqueItemsKeyword
MaxUniqueItemsKeyword
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();
}
}
}
}
Classe MaxUtf8ByteLengthKeyword
MaxUtf8ByteLengthKeyword
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;
}
}
}
Classe MinUtf8ByteLengthKeyword
MinUtf8ByteLengthKeyword
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;
}
}
}
Updated about 2 years ago