JSON Schema
This page shows the published JSON Schema as one formatted document. Use the chevrons to fold objects and arrays, follow highlighted objects and $ref values within the schema, and use the book icons to open definition guides. You can also open the raw schema file.
Version naming: the published v0.2 schema retains matspec for compatibility. The proposed v0.3 direction is "matjson": {"profile": "matspec", "version": "0.3"}, so profile identity and version remain explicit.
{ "$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://matjson.org/schema/matspec/0.2.10/schema.json",
"title": "MatSpecJSON v0.2 (schema patch 0.2.10 - permissive only)",
"description": "Material specification acceptance criteria. Encodes the limits a mill test report must satisfy, with a clause citation on every value.",
"type": "object",
"required": [ "matspec",
"specification",
"provenance",
"grades" ],
"additionalProperties": false,
"properties": { "matspec": { "const": "0.2",
"description": "Format version and type discriminator. Refuse files with an unrecognised version rather than partially reading them." },
"specification": { "type": "object",
"required": [ "designation",
"organization",
"title",
"edition" ],
"properties": { "designation": { "type": "string" },
"organization": { "type": "string" },
"adopted_from": { "type": "object",
"properties": { "organization": { "type": "string" },
"designation": { "type": "string" } } },
"title": { "type": "string" },
"edition": { "type": "string" },
"publication": { "type": "string" },
"product_forms": { "type": "array",
"items": { "type": "string" } },
"kind": { "enum": [ "product",
"general_requirements" ],
"default": "product",
"description": "A general-requirements specification imposes criteria that apply THROUGH a product specification. It has no grades of its own and must never be selected as the governing document for a grade." } } },
"provenance": { "type": "object",
"required": [ "source",
"extracted",
"method" ],
"properties": { "source": { "type": "string" },
"locator": { "type": "object" },
"extracted": { "type": "string",
"format": "date" },
"method": { "enum": [ "manual",
"assisted",
"automated" ] },
"extracted_scope": { "type": "string" } },
"description": "Where the data came from. Intrinsic to the file. Distinct from verification, which is a claim about accuracy made by a consumer and is deliberately not modelled here." },
"references": { "type": "array",
"items": { "type": "object",
"required": [ "designation",
"role" ],
"properties": { "designation": { "type": "string" },
"role": { "type": "string" },
"note": { "type": "string" } } } },
"manufacturing": { "type": "object" },
"grades": { "type": "array",
"minItems": 0,
"items": { "$ref": "#/$defs/grade" },
"description": " A general-requirements specification (SA-20, SA-450, SA-530, SA-1016, SA-480, SA-960, SA-961) defines no grades and carries an empty array. Declare it with specification.kind = 'general_requirements'." },
"required_tests": { "oneOf": [ { "$ref": "#/$defs/not_extracted" },
{ "type": "array",
"items": { "$ref": "#/$defs/test" } } ] },
"supplementary_requirements": { "oneOf": [ { "$ref": "#/$defs/not_extracted" },
{ "type": "array",
"items": { "type": "object" } } ] },
"notes": { "type": "object",
"description": "Note id to text. Values reference these by id.",
"additionalProperties": { "type": "string" },
"propertyNames": { "pattern": "^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$",
"description": "MatSpecJSON note identifiers use lowercase snake_case." } },
"extensions": { "type": "object",
"description": "Implementation-specific data. NOT part of the conformance surface - a conforming reader ignores it. Use this for workflow state that belongs to your process rather than to the specification: review status, internal ids, routing. Verification of a file's accuracy lives here or outside the file entirely, because it is an assertion about an instance by a party in a workflow, not a property of the specification being described. MatJSON-controlled extension keys use lowercase snake_case; exact external/domain identifiers may retain their published notation inside extension payloads.",
"additionalProperties": true,
"propertyNames": { "pattern": "^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$",
"description": "Extension namespace keys use lowercase snake_case." } } },
"$defs": { "not_extracted": { "type": "object",
"required": [ "extracted",
"note" ],
"description": "An explicit gap. Distinguishes 'not captured' from 'no requirement'.",
"properties": { "extracted": { "const": false },
"note": { "type": "string" } } },
"unit": { "enum": [ "wt%",
"%",
"ksi",
"MPa",
"ft-lb",
"J",
"in",
"mm",
"mils",
"HBW",
"HRB",
"HRC",
"HV",
"degF",
"degC",
"ppm",
"count" ] },
"value": { "type": "object",
"description": "One acceptance bound. Either min or max may be null; a citation is mandatory.",
"required": [ "unit",
"source" ],
"additionalProperties": false,
"properties": { "min": { "type": [ "number",
"null" ] },
"max": { "type": [ "number",
"null" ] },
"unit": { "$ref": "#/$defs/unit" },
"source": { "type": "string",
"minLength": 1 },
"basis": { "enum": [ "heat",
"product",
"both" ] },
"applies_when": { "$ref": "#/$defs/applies_when" },
"notes": { "type": "array",
"items": { "type": "string" } },
"balance": { "type": "boolean",
"description": "Element makes up the remainder. min and max do not apply and must be null." } },
"allOf": [ { "if": { "properties": { "balance": { "const": true } },
"required": [ "balance" ] },
"then": { "properties": { "min": { "type": "null" },
"max": { "type": "null" } } } } ] },
"value_set": { "type": "object",
"description": "Sibling values in different unit systems. Not conversions - both are specified in the source.",
"required": [ "values" ],
"properties": { "values": { "type": "array",
"minItems": 1,
"items": { "$ref": "#/$defs/value" } } } },
"band": { "type": "object",
"properties": { "min": { "type": "number" },
"max": { "type": "number" },
"unit": { "$ref": "#/$defs/unit" },
"inclusive": { "enum": [ "min",
"max",
"both",
"none" ],
"description": "Which end is inclusive. 'over 2 in. to 4 in., incl' is min exclusive, max inclusive." } } },
"applies_when": { "type": "object",
"description": "Selector. Every present key must match for the value to apply.",
"additionalProperties": false,
"properties": { "thickness": { "$ref": "#/$defs/band" },
"diameter": { "$ref": "#/$defs/band" },
"temperature": { "$ref": "#/$defs/band" },
"product_form": { "type": "string" },
"condition": { "type": "string" },
"gauge_length": { "oneOf": [ { "type": "object",
"required": [ "value",
"unit" ],
"additionalProperties": false,
"properties": { "value": { "type": "number" },
"unit": { "$ref": "#/$defs/unit" } } },
{ "type": "object",
"required": [ "multiple",
"of" ],
"additionalProperties": false,
"description": "Gauge length as a multiple of a dimension - bolting states elongation 'in 4D'.",
"properties": { "multiple": { "type": "number" },
"of": { "enum": [ "diameter",
"thickness" ] } } } ] },
"specimen_size": { "type": "string" },
"orientation": { "enum": [ "longitudinal",
"transverse",
"any" ] },
"class": { "type": "string",
"description": "Property class, e.g. SA-193 Class 1, 1A, 2, 2B. The same grade carries different mechanicals per class." },
"temper": { "type": "string",
"description": "Temper designation, e.g. O61, H55, H80 for copper alloys." },
"size_designation": { "type": "string",
"description": "Free-text size band where the source does not express it numerically." },
"specified_minimum_uts": { "$ref": "#/$defs/band",
"description": "Band on the SPECIFIED MINIMUM ultimate tensile strength of the grade being supplied - not the measured UTS. SA-20 Supplementary Requirement S20 selects its carbon-equivalent maximum this way, and without it the four S20 bounds are indistinguishable to a checker." },
"test_method": { "type": "string",
"description": "The measurement method a bound is stated against, where a specification gives DIFFERENT acceptance values for different methods of measuring the same property. SB-171 Table 3 is the case: it carries two yield strength columns, and for C61400 they differ (30 ksi versus 28 ksi at 0.2 % offset). Without this selector the two bounds are indistinguishable and a checker cannot choose. Distinct from the common situation where a specification permits either method against ONE value - that needs no selector." } } },
"grade": { "type": "object",
"required": [ "id",
"designation",
"chemistry" ],
"properties": { "id": { "type": "string" },
"designation": { "type": "object",
"properties": { "grade": { "type": [ "string",
"null" ] },
"class": { "type": [ "string",
"null" ] },
"type": { "type": [ "string",
"null" ] },
"uns": { "type": [ "string",
"null" ],
"pattern": "^[A-Z][0-9]{5}$" },
"uns_note": { "type": "string" } } },
"aliases": { "type": "array",
"items": { "type": "string" } },
"chemistry": { "oneOf": [ { "$ref": "#/$defs/not_extracted" },
{ "type": "object",
"required": [ "elements" ],
"properties": { "elements": { "type": "object",
"description": "Element symbol to an ARRAY of bounds - one element commonly has several, selected by thickness, diameter, class or analysis basis.",
"additionalProperties": { "type": "array",
"minItems": 1,
"items": { "$ref": "#/$defs/value_or_alternatives" },
"description": "Element symbol to an ARRAY of bounds. A member may be a value or an {'any_of': [...]} alternatives object - SA-516 Grade 60 manganese is a real case: Table 1 footnote C permits either of two ranges, and modelling them as two array members would make a checker demand both and reject conforming material." } },
"notes": { "type": "array",
"items": { "type": "string" } },
"pren_note": { "type": "string",
"description": "Composite chemistry bound (e.g. a PREN floor) that cannot be expressed as a value in v0.2. Must be checked manually." } } } ] },
"mechanical": { "oneOf": [ { "type": "null",
"description": "The specification imposes no mechanical property requirement. SA-179 is a real example: its tensile figures are EXPLANATORY NOTE design assumptions, not acceptance criteria." },
{ "$ref": "#/$defs/not_extracted" },
{ "type": "object",
"properties": { "tensile_strength": { "oneOf": [ { "type": "null",
"description": "The specification imposes no requirement for this property." },
{ "$ref": "#/$defs/not_extracted" },
{ "$ref": "#/$defs/value_set" } ] },
"yield_strength": { "oneOf": [ { "type": "null",
"description": "The specification imposes no requirement for this property." },
{ "$ref": "#/$defs/not_extracted" },
{ "$ref": "#/$defs/value_set" } ] },
"elongation": { "oneOf": [ { "type": "null",
"description": "The specification imposes no requirement for this property." },
{ "$ref": "#/$defs/not_extracted" },
{ "type": "array",
"items": { "$ref": "#/$defs/value" } } ] },
"reduction_of_area": { "oneOf": [ { "type": "null",
"description": "The specification imposes no requirement for this property." },
{ "$ref": "#/$defs/not_extracted" },
{ "type": "array",
"items": { "$ref": "#/$defs/value" } } ] } },
"additionalProperties": false,
"description": "Mechanical property requirements. additionalProperties is false so that a not_extracted object {extracted:false, note:...} matches exactly one branch of the oneOf. Without it the two branches are both satisfied and the oneOf fails - the object is ambiguous, not invalid. SB-366 is the case: it states no mechanical table of its own and sends properties to the source material specification." } ] },
"hardness": { "oneOf": [ { "type": "null",
"description": "The specification imposes no hardness requirement." },
{ "$ref": "#/$defs/not_extracted" },
{ "type": "array",
"items": { "$ref": "#/$defs/value_or_alternatives" } } ] },
"impact": { "oneOf": [ { "type": "null",
"description": "The specification imposes no impact requirement." },
{ "$ref": "#/$defs/not_extracted" },
{ "type": "object",
"required": [ "values" ],
"additionalProperties": false,
"description": "Impact requirements. 'applies_when' on each value carries the applicability trigger (thickness, specimen size, temperature).",
"properties": { "values": { "type": "array",
"items": { "$ref": "#/$defs/value" } },
"test_temperature": { "oneOf": [ { "$ref": "#/$defs/value_set" },
{ "type": "object",
"required": [ "extracted",
"note" ],
"additionalProperties": false,
"description": "The specification imposes an impact ENERGY requirement but states NO test temperature, or the temperature was not captured. SA-203 Para 7.2.1 is the case: energy is fixed at 20 ft-lbf [27 J] but temperature and orientation are 'a matter of agreement between the purchaser and supplier'. Without this the file cannot record that the Code is silent on temperature - which is the difference between a Code non-conformance and a contractual question.",
"properties": { "extracted": { "const": false },
"note": { "type": "string" },
"applicability": { "type": "string",
"description": "When the impact requirement applies at all, where it is conditional." } } } ] },
"applicability": { "type": "string",
"description": "When impact testing is triggered at all." },
"specimen_sizes": { "type": "array",
"items": { "type": "string" } } } } ] },
"heat_treatment": { "oneOf": [ { "type": "null" },
{ "$ref": "#/$defs/not_extracted" },
{ "type": "array",
"items": { "type": "object",
"required": [ "requirement",
"source" ],
"additionalProperties": false,
"properties": { "requirement": { "type": "string",
"description": "e.g. 'quenched and tempered', 'carbide solution treated', 'annealed'" },
"temperature": { "oneOf": [ { "$ref": "#/$defs/not_extracted",
"description": "The specification states no temperature for this grade - SA-182 Table 1 gives 'not specified' for F 6a - or the value was not captured." },
{ "$ref": "#/$defs/value_set" } ] },
"cooling": { "type": "string" },
"applies_when": { "$ref": "#/$defs/applies_when" },
"source": { "type": "string" },
"notes": { "type": "array",
"items": { "type": "string" } } } } } ] } } },
"test": { "type": "object",
"required": [ "test",
"required",
"source" ],
"properties": { "test": { "type": "string" },
"required": { "type": "boolean" },
"frequency": { "type": "object",
"properties": { "text": { "type": "string" },
"per": { "type": "string" },
"count": { "type": "number" } } },
"method": { "type": "string" },
"source": { "type": "string" } } },
"alternatives": { "type": "object",
"required": [ "any_of" ],
"additionalProperties": false,
"description": "Satisfying ANY member is acceptance. Specifications state hardness as '223 HBW or 96 HRB' - two scales, either acceptable, not two separate requirements.",
"properties": { "any_of": { "type": "array",
"minItems": 2,
"items": { "$ref": "#/$defs/value" } },
"note": { "type": "string" },
"applies_when": { "$ref": "#/$defs/applies_when",
"description": "Selector for the alternatives block as a whole. SA-193 Table 2 is the case: hardness is banded by diameter AND offered on two scales, so the any_of needs its own applies_when. Without it, banded alternatives are indistinguishable to a checker." } } },
"value_or_alternatives": { "oneOf": [ { "$ref": "#/$defs/value" },
{ "$ref": "#/$defs/alternatives" } ] } },
"x_matjson_profile": "matspec",
"x_matjson_status": "working_draft",
"x_matjson_naming_convention": "snake_case"}