C# Example of Meta-Schema v1
Validate payloads with Amazon Product Type Definition meta-schema instances.
Example Validator Implementation for .NET
For C# .NET applications, the Newtonsoft Json.NET Schema library supports JSON Schema Draft 2019-09 and custom vocabularies. The following example demonstrates how to utilize the Newtonsoft Json.NET Schema library to validate payloads with instances of the Amazon Product Type Definition Meta-Schema. There is no requirement to use this specific library or the example implementation. Amazon does not provide technical support for third-party JSON Schema libraries and this is provided as an example only.
Schema Configuration
When using the Newtonsoft Json.NET Schema to validate instances of the Amazon Product Type Definition Meta-Schema with custom vocabulary, the meta-schema is configured with a JSchemaResolver
and custom keyword validation is configured with JSchemaReaderSettings
when parsing instances of the Amazon Product Type Definition Meta-Schema.
Constants:
// $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";
Configure 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()
}
};
Load Meta-Schema Instance:
var luggageSchema = JSchema.Parse(File.ReadAllText(luggageSchemaPath), readerSettings);
Payload Validation
With an instance of the Amazon Product Type Definition Meta-Schema loaded as a JSchema
instance, payloads can be validated using the instance.
// Create a JObject for the payload (this can be constructed in code or read from a file).
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);
If the payload is valid, IsValid
will return true
with an empty list of error messages. Otherwise IsValid
will return false
with a list of error messages.
Keyword Validation
The Newtonsoft Json.NET Schema supports validating custom vocabulary by using classes that implementing the JsonValidator
interface and provide the validation logic.
Refer to https://www.newtonsoft.com/jsonschema/help/html/CustomJsonValidators.htm.
The following examples illustrate implementations of the JsonValidator
interface that validate the custom vocabulary in instances of the Amazon Product Type Definition Meta-Schema.
MaxUniqueItemsKeyword
Class
MaxUniqueItemsKeyword
Classusing 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();
}
}
}
}
MaxUtf8ByteLengthKeyword
Class
MaxUtf8ByteLengthKeyword
Classusing 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;
}
}
}
MinUtf8ByteLengthKeyword
Class
MinUtf8ByteLengthKeyword
Classusing 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 3 months ago