1. Purpose
This page describes Canon’s JSON format support, including canonicalization with sorted keys, type preservation, and JSON-specific features.
2. Canonicalization
Canon provides JSON canonicalization with sorted keys at all nesting levels.
Key features:
-
Alphabetically sorted object keys
-
Consistent indentation (configurable)
-
Proper escape sequences
-
No trailing commas
-
Unicode normalization
json = '{"z":3,"a":1,"nested":{"y":2,"x":1}}'
Canon.format(json, :json)
# => {"a":1,"nested":{"x":1,"y":2},"z":3}
# Keys sorted at all levels
3. Format defaults
| Dimension | Default Behavior |
|---|---|
|
|
|
|
|
|
Default diff mode: :by_object (tree-based semantic diff)
4. Match profiles for JSON
Canon provides predefined profiles optimized for JSON documents. Each profile configures preprocessing, match options, diff algorithm, and formatting.
4.1. strict profile
Purpose: Character-perfect JSON matching
Configuration:
{
preprocessing: :none,
diff_algorithm: :dom,
diff_mode: :by_object, # Tree-based diff output (JSON default)
match: {
text_content: :strict,
structural_whitespace: :strict,
key_order: :strict
}
}
Use when: Testing exact JSON serializer output, verifying JSON formatting compliance.
4.2. rendered profile
Purpose: Normalized JSON comparison
Configuration:
{
preprocessing: :none,
diff_algorithm: :dom,
diff_mode: :by_object,
match: {
text_content: :normalize,
structural_whitespace: :normalize,
key_order: :ignore # Allow unordered object keys
}
}
Use when: Comparing JSON data where key order and whitespace don’t matter.
4.3. spec_friendly profile
Purpose: Test-friendly comparison for RSpec
Configuration:
{
preprocessing: :normalize,
diff_algorithm: :dom,
diff_mode: :by_object,
match: {
text_content: :normalize,
structural_whitespace: :ignore,
key_order: :ignore
}
}
Use when: Writing RSpec tests for JSON generation, testing semantic JSON correctness. Most common for JSON testing.
4.4. content_only profile
Purpose: Maximum tolerance - only values matter
Configuration:
{
preprocessing: :normalize,
diff_algorithm: :dom,
diff_mode: :by_object,
match: {
text_content: :normalize,
structural_whitespace: :ignore,
key_order: :ignore
}
}
Use when: Only JSON structure and values need to match, maximum flexibility for formatting and key order.
5. JSON-specific features
5.1. Key ordering
Object keys are sorted alphabetically for consistent comparison.
// Unordered input
{
"name": "Alice",
"age": 30,
"city": "NYC"
}
// Canonicalized output (keys sorted)
{
"age": 30,
"city": "NYC",
"name": "Alice"
}
This ensures that two JSON objects with the same data but different key order are compared correctly when key_order: :ignore is used.
5.2. Type preservation
Distinguishes between numbers, strings, booleans, and null.
{
"string": "123",
"number": 123,
"boolean": true,
"null": null,
"float": 123.45
}
These values are treated as different types and won’t be considered equivalent:
* "123" (string) ≠ 123 (number)
* true (boolean) ≠ "true" (string)
* null ≠ "null" (string)
6. Usage examples
6.1. Basic JSON comparison
json1 = File.read("config1.json")
json2 = File.read("config2.json")
Canon::Comparison.equivalent?(json1, json2)
6.3. Test-friendly JSON comparison
expect(actual_json).to be_json_equivalent_to(expected_json)
.with_profile(:spec_friendly)
7. Common JSON comparison scenarios
7.1. API response comparison
# Compare API responses ignoring key order
Canon::Comparison.equivalent?(response1, response2,
match: {
key_order: :ignore,
text_content: :normalize
},
verbose: true
)
7.2. Configuration file comparison
# Compare config files with flexible matching
Canon::Comparison.equivalent?(config1, config2,
match_profile: :spec_friendly,
verbose: true
)
7.3. Array order sensitivity
// File 1
{"items": [1, 2, 3]}
// File 2
{"items": [3, 2, 1]}
These are NOT equivalent because array order matters in JSON. Arrays are ordered sequences, unlike objects.
If you need unordered array comparison, you’ll need to sort the arrays before comparison or use custom logic.
8. JSON quirks and edge cases
8.1. Number precision
{"value": 1.0}
{"value": 1}
These may be treated as equivalent or different depending on the JSON parser. Canon preserves the distinction between integers and floats.
9. See also
-
Comparison Pipeline - Understanding the 4 layers
-
Match Options - All matching options
-
Choosing Configuration - Decision guide
-
Format Support - Overview of all formats
-
YAML Format - YAML-specific features (similar to JSON)
-
XML Format - XML-specific features