1. Purpose
Canon’s 4-layer architecture provides powerful flexibility, but this can be overwhelming. This guide helps you choose the right configuration for your use case through decision trees, use case scenarios, and practical recommendations.
2. Quick Decision Tree
graph TD
Start[What are you comparing?] --> Similar{Similar<br/>structure?}
Similar -->|Yes| Fast{Need<br/>speed?}
Similar -->|No| Semantic[Use Semantic Algorithm]
Fast -->|Yes| DOM[Use DOM Algorithm]
Fast -->|No| Semantic
DOM --> Format{Care about<br/>formatting?}
Semantic --> Format
Format -->|Yes| Strict[strict profile]
Format -->|No| SpecFriendly[spec_friendly profile]
Strict --> Output1[by_line mode]
SpecFriendly --> Output2{Want<br/>operations?}
Output2 -->|Yes| ByObject[by_object mode]
Output2 -->|No| ByLine[by_line mode]
style DOM fill:#fff4e1
style Semantic fill:#e1f5ff
style Strict fill:#ffe1f5
style SpecFriendly fill:#ffe1f5
style ByObject fill:#e1ffe1
style ByLine fill:#e1ffe1
3. Layer-by-Layer Decision Guide
3.1. Layer 1: Preprocessing
Question: How should documents be normalized before comparison?
| Choose | When | Example |
|---|---|---|
none |
Documents already in comparable form, no normalization needed |
Comparing canonicalized XML files |
c14n |
Testing XML canonicalization implementations |
Validating C14N output |
normalize |
Whitespace differences are irrelevant |
Comparing generated vs handwritten XML |
format |
Want to compare structure, ignore all formatting |
Comparing minified vs formatted JSON |
Default: none (no preprocessing)
Ruby API:
Canon::Comparison.equivalent?(doc1, doc2,
preprocessing: :normalize # or :c14n, :format
)
CLI:
canon diff file1.xml file2.xml --preprocessing normalize
3.2. Layer 2: Algorithm Selection
Question: What comparison strategy fits your documents?
| Choose | When | Characteristics |
|---|---|---|
dom |
• Similar document structure |
• Fast |
semantic |
• Restructured documents |
• Slower |
Default: dom (stable algorithm)
Decision Matrix:
| Scenario | DOM | Semantic |
|---|---|---|
Documents have same structure |
✓ |
✓ |
Documents are reordered |
✗ |
✓ |
Need fast comparison |
✓ |
✗ |
Need move detection |
✗ |
✓ |
Production use |
✓ |
⚠ |
Large documents (> 100KB) |
✓ |
✗ |
Ruby API:
Canon::Comparison.equivalent?(doc1, doc2,
diff_algorithm: :dom # or :semantic
)
CLI:
canon diff file1.xml file2.xml --diff-algorithm semantic
3.3. Layer 3: Match Options
Question: How strict should comparison be?
3.3.1. Using Match Profiles (Recommended)
| Profile | Use When |
|---|---|
strict |
Exact matching required. Everything must match exactly including whitespace, attribute order, comments. |
rendered |
Comparing rendered output. Simulates browser/CSS rendering - ignores formatting but keeps content strict. |
spec_friendly |
Writing tests. Ignores formatting differences, focuses on content and structure. |
content_only |
Content comparison only. Ignores all structural and formatting differences. |
Default: strict (exact matching)
Ruby API:
Canon::Comparison.equivalent?(doc1, doc2,
match_profile: :spec_friendly # or :strict, :rendered, :content_only
)
CLI:
canon diff file1.xml file2.xml --match-profile spec_friendly
3.3.2. Custom Match Dimensions
For fine-grained control, configure individual dimensions:
Canon::Comparison.equivalent?(doc1, doc2,
match: {
text_content: :normalize, # normalize, strict, ignore
structural_whitespace: :ignore, # ignore, normalize, strict
attribute_order: :ignore, # ignore, strict (XML/HTML)
attribute_values: :normalize, # normalize, strict, ignore
comments: :ignore, # ignore, normalize, strict
whitespace_type: :strict # strict (default), normalize
}
)
Remember: Match options behave differently with each algorithm! See Algorithm-Specific Behavior.
3.3.3. Whitespace Type Sensitivity
By default, Canon distinguishes between different Unicode whitespace types (e.g. regular space U+0020 vs non-breaking space U+00A0 vs ideographic space U+3000). This catches accidental insertion of wrong whitespace characters.
Use whitespace_type: :normalize when all Unicode whitespace variants should
be treated as equivalent (e.g. when output from different tools may use
different whitespace types for the same visual result).
3.4. Layer 4: Diff Formatting
Question: How should differences be displayed?
3.4.1. Choosing Diff Mode
| Mode | Best For | Output Type |
|---|---|---|
by_line |
• Traditional diffs |
Line-based diff similar to |
by_object |
• Tree structure view |
Tree-based with operations (INSERT, DELETE, UPDATE, MOVE) |
Default: by_line for DOM, by_object for Semantic
Natural Fits: * DOM + by_line = Traditional positional diff * Semantic + by_object = Operation-based tree diff
Ruby API:
Canon::Comparison.equivalent?(doc1, doc2,
diff_mode: :by_object, # or :by_line
verbose: true # Enable diff output
)
CLI:
canon diff file1.xml file2.xml --diff-mode by-object --verbose
4. Use Case Scenarios
4.1. Scenario 1: Unit Testing XML Generation
Requirement: Test that code generates correct XML, ignoring formatting
Configuration:
expect(actual_xml).to be_equivalent_to(expected_xml).with_options(
preprocessing: :normalize, # Ignore formatting differences
diff_algorithm: :dom, # Fast, stable
match_profile: :spec_friendly, # Test-friendly
verbose: true # Show diffs on failure
)
Why:
* normalize handles inconsistent whitespace
* dom is fast and stable for tests
* spec_friendly focuses on content, not formatting
* verbose helps debug failures
4.2. Scenario 2: Comparing API Responses
Requirement: Compare JSON responses, key order doesn’t matter
Configuration:
Canon::Comparison.equivalent?(response1, response2,
diff_algorithm: :dom,
match: {
key_order: :ignore, # JSON key order irrelevant
text_content: :normalize # Normalize string values
},
verbose: true,
diff_mode: :by_object # Tree view of differences
)
Why:
* key_order: :ignore handles JSON object key reordering
* by_object shows structured diff
* dom is sufficient for API responses
4.3. Scenario 3: Detecting Document Restructuring
Requirement: Find what changed when document is reorganized
Configuration:
result = Canon::Comparison.equivalent?(old_doc, new_doc,
diff_algorithm: :semantic, # Detect moves
match_profile: :spec_friendly, # Ignore formatting
verbose: true,
diff_mode: :by_object # See operations
)
# Analyze operations
puts "Moves: #{result.statistics.moves}"
puts "Updates: #{result.statistics.updates}"
Why:
* semantic algorithm detects moves and restructuring
* by_object shows operation-level changes
* Statistics provide quantitative analysis
4.4. Scenario 4: Code Review Diff
Requirement: Traditional diff for reviewing changes
Configuration:
canon diff old.xml new.xml \
--diff-algorithm dom \
--match-profile spec_friendly \
--diff-mode by_line \
--verbose \
--use-color \
--context-lines 3
Why:
* dom + by_line gives traditional diff
* context_lines provides context
* Colors improve readability
4.5. Scenario 5: Canonicalization Testing
Requirement: Test C14N implementation
Configuration:
Canon::Comparison.equivalent?(doc, canonical_doc,
preprocessing: :c14n, # Apply canonicalization
diff_algorithm: :dom,
match_profile: :strict, # Exact match required
verbose: true
)
Why:
* c14n preprocessing applies canonicalization
* strict profile ensures exact match
* Tests that canonicalization produces correct output
4.6. Scenario 6: Content-Only Comparison
Requirement: Compare only text content, ignore all structure
Configuration:
Canon::Comparison.equivalent?(doc1, doc2,
preprocessing: :format, # Normalize structure first
diff_algorithm: :semantic, # Better for structure-independent
match_profile: :content_only, # Ignore all structure
verbose: true,
diff_mode: :by_object
)
Why:
* content_only profile ignores structure
* semantic algorithm better at structure-independent comparison
* format preprocessing normalizes before comparison
5. Layer Interaction Matrix
This table shows recommended configurations for common scenarios:
| Use Case | Layer 1 | Layer 2 | Layer 3 | Layer 4 | Notes |
|---|---|---|---|---|---|
Unit tests (similar structure) |
normalize |
dom |
spec_friendly |
by_line |
Fast, test-friendly |
Unit tests (any structure) |
normalize |
semantic |
spec_friendly |
by_object |
Handles restructuring |
API response comparison |
none |
dom |
custom |
by_object |
Configure key_order |
Document evolution tracking |
none |
semantic |
rendered |
by_object |
Detect operations |
Code review |
none |
dom |
strict |
by_line |
Traditional diff |
C14N testing |
c14n |
dom |
strict |
by_line |
Exact match |
Content extraction testing |
format |
semantic |
content_only |
by_object |
Structure-independent |
Regression testing |
normalize |
dom |
spec_friendly |
by_line |
Stable, fast |
6. Common Configuration Patterns
6.1. Pattern 1: Fast Test Assertion
# Minimal configuration for speed
Canon::Comparison.equivalent?(expected, actual,
match_profile: :spec_friendly
)
# Uses defaults: no preprocessing, dom algorithm, by_line output
6.2. Pattern 2: Comprehensive Analysis
# Full analysis with all features
result = Canon::Comparison.equivalent?(doc1, doc2,
preprocessing: :normalize,
diff_algorithm: :semantic,
match_profile: :spec_friendly,
verbose: true,
diff_mode: :by_object,
use_color: true,
context_lines: 5,
show_legend: true
)
# Access rich data
puts result.operations
puts result.statistics
7. Anti-Patterns to Avoid
7.1. Anti-Pattern 1: Over-Configuration
# DON'T: Conflicting settings
Canon::Comparison.equivalent?(doc1, doc2,
preprocessing: :c14n, # Canonicalizes
match: {
structural_whitespace: :strict # Conflicts with c14n
}
)
# DO: Choose one approach
Canon::Comparison.equivalent?(doc1, doc2,
preprocessing: :c14n # Handles normalization
)
7.2. Anti-Pattern 2: Wrong Algorithm/Mode Combination
# SUBOPTIMAL: Loses semantic information
Canon::Comparison.equivalent?(doc1, doc2,
diff_algorithm: :semantic,
diff_mode: :by_line # Doesn't show operations well
)
# BETTER: Use natural fit
Canon::Comparison.equivalent?(doc1, doc2,
diff_algorithm: :semantic,
diff_mode: :by_object # Shows operations clearly
)
7.3. Anti-Pattern 3: Unnecessary Semantic Algorithm
# SLOW: Semantic not needed for similar documents
Canon::Comparison.equivalent?(doc1, doc2,
diff_algorithm: :semantic # Overkill if no restructuring
)
# FASTER: Use DOM for similar structures
Canon::Comparison.equivalent?(doc1, doc2,
diff_algorithm: :dom # Fast for similar docs
)
8. Performance Considerations
8.1. Performance Impact by Layer
| Layer | Low Impact | Medium Impact | High Impact |
|---|---|---|---|
Layer 1 |
none |
normalize, format |
c14n (complex documents) |
Layer 2 |
dom |
— |
semantic |
Layer 3 |
Any profile |
— |
Complex custom dimensions |
Layer 4 |
by_line |
by_object (small docs) |
by_object (large docs) |
8.2. Optimization Guidelines
For Speed:
Canon::Comparison.equivalent?(doc1, doc2,
preprocessing: :none, # Skip preprocessing
diff_algorithm: :dom, # Fast algorithm
match_profile: :strict, # Simple matching
diff_mode: :by_line # Fast output
)
For Intelligence (accepting slower performance):
Canon::Comparison.equivalent?(doc1, doc2,
preprocessing: :normalize, # Normalize first
diff_algorithm: :semantic, # Intelligent algorithm
diff_mode: :by_object # Rich output
)
9. Migration Checklist
When changing configuration:
9.1. Changing Algorithm (DOM → Semantic)
-
Update
diff_algorithmoption -
Consider changing
diff_modetoby_object -
Remove or update
attribute_orderexpectations -
Update test assertions for operation-based output
-
Accept slower performance
-
Review move detection impact
10. Using Configuration Profiles
If you use the same configuration across multiple gems or projects, consider using a configuration profile instead of repeating settings. A single line replaces dozens of configuration calls:
# Instead of 60+ lines of per-format settings:
Canon::Config.instance.profile = :metanorma
# Or load a custom profile from a local YAML file:
Canon::Config.instance.profile = "config/my_canon_profile.yml"
Profiles bundle all layers (preprocessing, match profile, diff settings, whitespace element lists) into a named preset defined in YAML. Custom file profiles can inherit from built-in profiles.
See Configuration Profiles for full documentation.
11. See Also
-
Configuration Profiles - Named config presets
-
Comparison Pipeline - Understanding the 4 layers
-
Algorithms - Detailed algorithm documentation
-
Algorithm-Specific Behavior - How algorithms differ
-
Algorithm-Specific Output - Output format differences
-
Match Options - All matching options
-
Diff Formatting - Formatting options