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 15 days ago