diff --git a/schema/json/metaschema-datatypes.json b/schema/json/metaschema-datatypes.json
index bed54f6..44d8fea 100644
--- a/schema/json/metaschema-datatypes.json
+++ b/schema/json/metaschema-datatypes.json
@@ -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.",
diff --git a/schema/xml/metaschema-datatypes.xsd b/schema/xml/metaschema-datatypes.xsd
index 601d3c7..b36f17a 100644
--- a/schema/xml/metaschema-datatypes.xsd
+++ b/schema/xml/metaschema-datatypes.xsd
@@ -68,17 +68,15 @@
- A real number expressed using a whole and optional fractional part
- separated by a period.
+
+ 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).
+
-
-
-
- This pattern ensures that leading and trailing whitespace is
- disallowed. This helps to even the user experience between implementations
- related to whitespace.
-
-
+
+
diff --git a/test/decimal-type/.gitignore b/test/decimal-type/.gitignore
new file mode 100644
index 0000000..aea0a90
--- /dev/null
+++ b/test/decimal-type/.gitignore
@@ -0,0 +1,5 @@
+# Compiled Java classes
+*.class
+
+# Maven build output
+target/
diff --git a/test/decimal-type/README.md b/test/decimal-type/README.md
new file mode 100644
index 0000000..cff884d
--- /dev/null
+++ b/test/decimal-type/README.md
@@ -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
+```
diff --git a/test/decimal-type/XmlValidator.java b/test/decimal-type/XmlValidator.java
new file mode 100644
index 0000000..3421b2b
--- /dev/null
+++ b/test/decimal-type/XmlValidator.java
@@ -0,0 +1,62 @@
+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;
+import java.io.File;
+import org.xml.sax.SAXNotRecognizedException;
+import org.xml.sax.SAXNotSupportedException;
+
+public class XmlValidator {
+ public static void main(String[] args) {
+ if (args.length < 2) {
+ System.out.println("Usage: java XmlValidator ");
+ System.exit(1);
+ }
+
+ String schemaPath = args[0];
+ String xmlPath = args[1];
+
+ File schemaFile = new File(schemaPath);
+ File xmlFile = new File(xmlPath);
+
+ if (!schemaFile.exists()) {
+ System.out.println("Schema file not found: " + schemaPath);
+ System.exit(1);
+ }
+ if (!xmlFile.exists()) {
+ System.out.println("XML file not found: " + xmlPath);
+ System.exit(1);
+ }
+
+ try {
+ SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+
+ // Harden against XXE / SSRF-style external resolution
+ factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
+ try {
+ factory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
+ factory.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
+ } catch (IllegalArgumentException | SAXNotRecognizedException | SAXNotSupportedException ex) {
+ // Some JAXP implementations don't support these properties
+ System.err.println("Warning: could not set external access restrictions: " + ex.getMessage());
+ }
+
+ Schema schema = factory.newSchema(schemaFile);
+ Validator validator = schema.newValidator();
+
+ try {
+ validator.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
+ validator.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
+ } catch (IllegalArgumentException ex) {
+ System.err.println("Warning: could not set validator external access restrictions: " + ex.getMessage());
+ }
+
+ validator.validate(new StreamSource(xmlFile));
+ System.out.println(xmlPath + " is VALID");
+ } catch (Exception e) {
+ System.err.println(xmlPath + " is INVALID: " + e.getMessage());
+ System.exit(1);
+ }
+ }
+}
diff --git a/test/decimal-type/decimal-test.json b/test/decimal-type/decimal-test.json
new file mode 100644
index 0000000..e743324
--- /dev/null
+++ b/test/decimal-type/decimal-test.json
@@ -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"
+ }
+ },
+ "required": ["id", "description", "expected", "value"]
+ }
+ }
+ },
+ "required": ["test-cases"]
+ }
+ },
+ "required": ["decimal-tests"]
+}
diff --git a/test/decimal-type/decimal-test.xsd b/test/decimal-type/decimal-test.xsd
new file mode 100644
index 0000000..8c263b1
--- /dev/null
+++ b/test/decimal-type/decimal-test.xsd
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 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).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/decimal-type/json-invalid-cases.json b/test/decimal-type/json-invalid-cases.json
new file mode 100644
index 0000000..9e147d8
--- /dev/null
+++ b/test/decimal-type/json-invalid-cases.json
@@ -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"
+ }
+ }
+}
diff --git a/test/decimal-type/json-test-string-value.json b/test/decimal-type/json-test-string-value.json
new file mode 100644
index 0000000..ec87a93
--- /dev/null
+++ b/test/decimal-type/json-test-string-value.json
@@ -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 "
+ }
+ ]
+ }
+}
diff --git a/test/decimal-type/json-valid-cases.json b/test/decimal-type/json-valid-cases.json
new file mode 100644
index 0000000..0e8264a
--- /dev/null
+++ b/test/decimal-type/json-valid-cases.json
@@ -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
+ }
+ ]
+ }
+}
diff --git a/test/decimal-type/pom.xml b/test/decimal-type/pom.xml
new file mode 100644
index 0000000..28ef2ef
--- /dev/null
+++ b/test/decimal-type/pom.xml
@@ -0,0 +1,44 @@
+
+
+ 4.0.0
+
+ gov.nist.secauto.metaschema.test
+ decimal-type-test
+ 1.0.0-SNAPSHOT
+ jar
+
+ Decimal Type Validation Tests
+ Test utilities for validating decimal datatype constraints in XML and JSON
+
+
+ 11
+ 11
+ UTF-8
+
+
+
+ ${project.basedir}
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.11.0
+
+
+ *.java
+
+
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+ 3.1.0
+
+ XmlValidator
+
+
+
+
+
diff --git a/test/decimal-type/xml-invalid-cases.xml b/test/decimal-type/xml-invalid-cases.xml
new file mode 100644
index 0000000..d1debcf
--- /dev/null
+++ b/test/decimal-type/xml-invalid-cases.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+ 0
+
+
+
diff --git a/test/decimal-type/xml-test-leading-plus.xml b/test/decimal-type/xml-test-leading-plus.xml
new file mode 100644
index 0000000..e92f445
--- /dev/null
+++ b/test/decimal-type/xml-test-leading-plus.xml
@@ -0,0 +1,8 @@
+
+
+
+ +12.34
+
+
diff --git a/test/decimal-type/xml-test-leading-whitespace.xml b/test/decimal-type/xml-test-leading-whitespace.xml
new file mode 100644
index 0000000..7335f96
--- /dev/null
+++ b/test/decimal-type/xml-test-leading-whitespace.xml
@@ -0,0 +1,8 @@
+
+
+
+ 12.34
+
+
diff --git a/test/decimal-type/xml-test-leading-zero.xml b/test/decimal-type/xml-test-leading-zero.xml
new file mode 100644
index 0000000..0d8b134
--- /dev/null
+++ b/test/decimal-type/xml-test-leading-zero.xml
@@ -0,0 +1,8 @@
+
+
+
+ 01.23
+
+
diff --git a/test/decimal-type/xml-test-no-leading-digit.xml b/test/decimal-type/xml-test-no-leading-digit.xml
new file mode 100644
index 0000000..3a2cbc0
--- /dev/null
+++ b/test/decimal-type/xml-test-no-leading-digit.xml
@@ -0,0 +1,8 @@
+
+
+
+ .47
+
+
diff --git a/test/decimal-type/xml-test-trailing-decimal.xml b/test/decimal-type/xml-test-trailing-decimal.xml
new file mode 100644
index 0000000..9dbfb72
--- /dev/null
+++ b/test/decimal-type/xml-test-trailing-decimal.xml
@@ -0,0 +1,8 @@
+
+
+
+ 123.
+
+
diff --git a/test/decimal-type/xml-valid-cases.xml b/test/decimal-type/xml-valid-cases.xml
new file mode 100644
index 0000000..e2d6a1a
--- /dev/null
+++ b/test/decimal-type/xml-valid-cases.xml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+ 12
+
+
+ -42
+
+
+ 0
+
+
+
+
+ 12.34
+
+
+ -12.34
+
+
+ 0.001
+
+
+ 0.0
+
+
+
+
+ 1e3
+
+
+ 1E3
+
+
+ 1e+3
+
+
+ 1e-3
+
+
+ -2.5e-10
+
+
+
diff --git a/website/content/specification/datatypes.md b/website/content/specification/datatypes.md
index a58388d..0decf63 100644
--- a/website/content/specification/datatypes.md
+++ b/website/content/specification/datatypes.md
@@ -315,14 +315,14 @@ In JSON Schema, this is represented as:
### decimal
-A real number expressed using a whole and optional fractional part separated by a period.
+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`).
-In XML Schema this is represented as a restriction on the built-in type [decimal](https://www.w3.org/TR/xmlschema11-2/#decimal) as follows:
+In XML Schema this is represented as a restriction on the built-in type [double](https://www.w3.org/TR/xmlschema11-2/#double) as follows:
```XML
-
-
+
+
```
@@ -331,8 +331,7 @@ In JSON Schema, this is represented as:
```JSON
{
- "type": "number",
- "pattern": "(\\+|-)?([0-9]+(\\.[0-9]*)?|\\.[0-9]+)"
+ "type": "number"
}
```
### email-address