Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions schema/json/metaschema-datatypes.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,8 @@
"pattern": "^-?P([0-9]+D(T(([0-9]+H([0-9]+M)?(([0-9]+|[0-9]+(\\.[0-9]+)?)S)?)|([0-9]+M(([0-9]+|[0-9]+(\\.[0-9]+)?)S)?)|([0-9]+|[0-9]+(\\.[0-9]+)?)S))?)|T(([0-9]+H([0-9]+M)?(([0-9]+|[0-9]+(\\.[0-9]+)?)S)?)|([0-9]+M(([0-9]+|[0-9]+(\\.[0-9]+)?)S)?)|([0-9]+|[0-9]+(\\.[0-9]+)?)S)$"
},
"DecimalDatatype": {
"description": "A real number expressed using a whole and optional fractional part separated by a period.",
"type": "number",
"pattern": "^(\\+|-)?([0-9]+(\\.[0-9]*)?|\\.[0-9]+)$"
"description": "A real number expressed using a whole and optional fractional part separated by a period, with optional exponential notation. No leading '+' is allowed.",
"type": "number"
},
"EmailAddressDatatype": {
"description": "An email address string formatted according to RFC 6531.",
Expand Down
18 changes: 8 additions & 10 deletions schema/xml/metaschema-datatypes.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -68,17 +68,15 @@

<xs:simpleType name="DecimalDatatype">
<xs:annotation>
<xs:documentation>A real number expressed using a whole and optional fractional part
separated by a period.</xs:documentation>
<xs:documentation>
A real number expressed using a whole and optional fractional part
separated by a period, with optional exponential notation.
No leading '+' is allowed. Leading zeros are not allowed except
for the integer 0 itself or numbers less than 1 (e.g., 0.5).
</xs:documentation>
Comment thread
coderabbitai[bot] marked this conversation as resolved.
</xs:annotation>
<xs:restriction base="xs:decimal">
<xs:pattern value="\S(.*\S)?">
<xs:annotation>
<xs:documentation>This pattern ensures that leading and trailing whitespace is
disallowed. This helps to even the user experience between implementations
related to whitespace.</xs:documentation>
</xs:annotation>
</xs:pattern>
<xs:restriction base="xs:double">
<xs:pattern value="-?(0|[1-9][0-9]*)(\.[0-9]+)?([eE][+-]?[0-9]+)?"/>
</xs:restriction>
</xs:simpleType>

Expand Down
5 changes: 5 additions & 0 deletions test/decimal-type/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Compiled Java classes
*.class

# Maven build output
target/
68 changes: 68 additions & 0 deletions test/decimal-type/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Decimal Datatype Test Cases

This directory contains test schemas and content examples to validate the decimal datatype behavior as defined in PR #135.

## Schemas

- `decimal-test.xsd` - XML Schema with inline DecimalDatatype using pattern `-?(0|[1-9][0-9]*)(\.[0-9]+)?([eE][+-]?[0-9]+)?` and base type `xs:double`
- `decimal-test.json` - JSON Schema with inline DecimalDatatype using `type: "number"`

## Expected Behavior Matrix (Tested)

| Test Case | Example | JSON | XML |
|-----------|---------|:----:|:---:|
| Integers | `12` | ✓ | ✓ |
| Decimals | `12.34` | ✓ | ✓ |
| Positive with leading + | `+12.34` | ✗ | ✗ |
| Exponential notation | `1e3`, `1E+4`, `-2.5e-10` | ✓ | ✓ |
| Leading zeros | `01.23` | ✗ | ✗ |
| No leading digit | `.47` | ✗ | ✗ |
| Trailing decimal point | `123.` | ✗ | ✗ |
| Leading/trailing whitespace | ` 12.34 ` | ✓ | ✓ |

**Legend:** ✓ = Valid, ✗ = Invalid

### Notes

Both JSON and XML ignore/normalize whitespace around numeric values, so leading/trailing whitespace is accepted in both formats.

## Test Files

### XML Test Cases

- `xml-valid-cases.xml` - Contains values that SHOULD pass XML Schema validation
- `xml-invalid-cases.xml` - Contains commented-out invalid cases for individual testing

### JSON Test Cases

- `json-valid-cases.json` - Contains values that SHOULD pass JSON Schema validation
- `json-invalid-cases.json` - Documents invalid cases (many cannot be represented in valid JSON syntax)

## Key Differences Between JSON and XML

1. **Whitespace Handling**: Both XML Schema and JSON ignore whitespace around numeric values. XML Schema applies whitespace normalization (collapse) for `xs:double` before pattern matching. JSON ignores whitespace around values during parsing.

2. **Syntax Restrictions**: JSON syntax itself prohibits leading `+`, leading zeros (e.g., `01.23`), `.47`, and `123.` formats, making these parse errors rather than validation errors. The XML pattern explicitly rejects these as well.

## Running Validation Tests

### XML Validation

#### Using xmllint
```bash
xmllint --schema decimal-test.xsd xml-valid-cases.xml --noout
```

#### Using the Java XmlValidator (via Maven)
```bash
# Compile the validator
mvn compile

# Run validation on a specific XML file
mvn exec:java -Dexec.args="decimal-test.xsd xml-valid-cases.xml"
```

### JSON Validation (using ajv-cli)
```bash
ajv validate -s decimal-test.json -d json-valid-cases.json
```
29 changes: 29 additions & 0 deletions test/decimal-type/XmlValidator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import javax.xml.XMLConstants;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
import java.io.File;

public class XmlValidator {
public static void main(String[] args) {
if (args.length < 2) {
System.out.println("Usage: java XmlValidator <schema.xsd> <document.xml>");
System.exit(1);
}

String schemaPath = args[0];
String xmlPath = args[1];

try {
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = factory.newSchema(new File(schemaPath));
Validator validator = schema.newValidator();
validator.validate(new StreamSource(new File(xmlPath)));
System.out.println(xmlPath + " is VALID");
} catch (Exception e) {
System.out.println(xmlPath + " is INVALID: " + e.getMessage());
System.exit(1);
}
}
}
38 changes: 38 additions & 0 deletions test/decimal-type/decimal-test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "http://csrc.nist.gov/ns/metaschema/test/decimal/decimal-test-schema.json",
"$comment": "Schema for testing decimal datatype validation",
"type": "object",
"properties": {
"decimal-tests": {
"type": "object",
"properties": {
"test-cases": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"description": {
"type": "string"
},
"expected": {
"type": "string",
"enum": ["valid", "invalid"]
},
"value": {
"description": "A real number expressed using a whole and optional fractional part separated by a period, with optional exponential notation. No leading '+' is allowed.",
"type": "number"
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
},
"required": ["id", "description", "expected", "value"]
}
}
},
"required": ["test-cases"]
}
},
"required": ["decimal-tests"]
}
46 changes: 46 additions & 0 deletions test/decimal-type/decimal-test.xsd
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:m="http://csrc.nist.gov/ns/metaschema/test/decimal"
targetNamespace="http://csrc.nist.gov/ns/metaschema/test/decimal"
elementFormDefault="qualified">

<!-- Root element for testing -->
<xs:element name="decimal-tests">
<xs:complexType>
<xs:sequence>
<xs:element name="test-case" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="value">
<xs:simpleType>
<xs:annotation>
<xs:documentation>
A real number expressed using a whole and optional fractional part
separated by a period, with optional exponential notation.
No leading '+' is allowed. Leading zeros are not allowed except
for the integer 0 itself or numbers less than 1 (e.g., 0.5).
</xs:documentation>
</xs:annotation>
<xs:restriction base="xs:double">
<xs:pattern value="-?(0|[1-9][0-9]*)(\.[0-9]+)?([eE][+-]?[0-9]+)?"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:sequence>
<xs:attribute name="id" type="xs:string" use="required"/>
<xs:attribute name="description" type="xs:string" use="required"/>
<xs:attribute name="expected" use="required">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="valid"/>
<xs:enumeration value="invalid"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>

</xs:schema>
47 changes: 47 additions & 0 deletions test/decimal-type/json-invalid-cases.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"$schema": "decimal-test.json",
"_comment": "Invalid decimal test cases for JSON validation. These values SHOULD FAIL JSON Schema validation or are invalid JSON syntax.",
"_note": "JSON does not allow: leading +, leading/trailing whitespace, .47 syntax, 123. syntax. These would be syntax errors or string values.",
"decimal-tests": {
"test-cases": [
{
"id": "json-placeholder",
"description": "Placeholder - see comments below for invalid cases that cannot be represented in JSON",
"expected": "valid",
"value": 0
}
]
},
"_invalid_cases_documentation": {
"leading_plus": {
"example": "+12.34",
"reason": "JSON syntax does not allow leading + on numbers. This would be a parse error.",
"expected": "invalid"
},
"leading_zeros": {
"example": "01.23",
"reason": "JSON syntax does not allow leading zeros on numbers (except 0 itself). This would be a parse error.",
"expected": "invalid"
},
"no_leading_digit": {
"example": ".47",
"reason": "JSON syntax requires a digit before the decimal point. This would be a parse error.",
"expected": "invalid"
},
"trailing_decimal": {
"example": "123.",
"reason": "JSON syntax requires digits after the decimal point. This would be a parse error.",
"expected": "invalid"
},
"leading_whitespace": {
"example": " 12.34",
"reason": "If represented as a string \" 12.34\", it would fail type: number validation.",
"expected": "invalid"
},
"trailing_whitespace": {
"example": "12.34 ",
"reason": "If represented as a string \"12.34 \", it would fail type: number validation.",
"expected": "invalid"
}
}
}
13 changes: 13 additions & 0 deletions test/decimal-type/json-test-string-value.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"$schema": "decimal-test.json",
"decimal-tests": {
"test-cases": [
{
"id": "json-string-with-whitespace",
"description": "String value with whitespace should fail type: number",
"expected": "invalid",
"value": " 12.34 "
}
]
}
}
68 changes: 68 additions & 0 deletions test/decimal-type/json-valid-cases.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
{
"$schema": "decimal-test.json",
"_comment": "Valid decimal test cases for JSON validation. All values SHOULD pass JSON Schema validation with type: number",
"decimal-tests": {
"test-cases": [
{
"id": "json-int-positive",
"description": "Positive integer (e.g., 12)",
"expected": "valid",
"value": 12
},
{
"id": "json-int-negative",
"description": "Negative integer",
"expected": "valid",
"value": -42
},
{
"id": "json-int-zero",
"description": "Zero",
"expected": "valid",
"value": 0
},
{
"id": "json-dec-positive",
"description": "Positive decimal (e.g., 12.34)",
"expected": "valid",
"value": 12.34
},
{
"id": "json-dec-negative",
"description": "Negative decimal",
"expected": "valid",
"value": -12.34
},
{
"id": "json-dec-small",
"description": "Small decimal",
"expected": "valid",
"value": 0.001
},
{
"id": "json-exp-lower",
"description": "Exponential notation lowercase (e.g., 1e3) - Valid in JSON",
"expected": "valid",
"value": 1e3
},
{
"id": "json-exp-upper-plus",
"description": "Exponential notation uppercase with plus (e.g., 1E+4) - Valid in JSON",
"expected": "valid",
"value": 1E+4
},
{
"id": "json-exp-negative",
"description": "Exponential notation negative (e.g., -2.5e-10) - Valid in JSON",
"expected": "valid",
"value": -2.5e-10
},
{
"id": "json-decimal-no-leading-zero",
"description": "Decimal without unnecessary leading zeros (1.23 is valid; 01.23 is invalid JSON syntax)",
"expected": "valid",
"value": 1.23
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
]
}
}
Loading