Purpose
XSDVI (XML Schema Definition Visualizer) is a Ruby gem that transforms W3C XML Schema (XSD) files into interactive, hierarchical SVG diagrams. It provides a visual representation of complex XML Schema structures, making it easier to understand schema relationships, element hierarchies, attribute requirements, and data type definitions.
The tool parses XSD files and generates SVG diagrams that display:
-
Element structures with their types, cardinality, and attributes
-
Compositor relationships (sequence, choice, all)
-
Type hierarchies and inheritance
-
Identity constraints (key, keyref, unique)
-
Namespace information
-
Documentation annotations from the schema
-
Recursive references with loop detection
This is a pure Ruby port of the original Java XsdVi tool, providing the same functionality with modern Ruby idioms and gemification for easy integration into Ruby projects.
Features
XSD component visualization
-
Elements and Attributes: Visual boxes showing element names, types, cardinality (min..max occurrences), required/optional status, and namespace information
-
Compositors: Graphical representation of sequence (ordered), choice (alternatives), and all (unordered) element groups
-
Wildcards: Display of
<any>and<anyAttribute>with namespace constraints and processing modes -
Identity Constraints: Visualization of key, keyref, unique constraints with their selectors and fields
-
Type Information: Display of simple and complex type definitions, base types, and anonymous types
Interactive SVG output
-
Hierarchical Layout: Tree-structure visualization with proper indentation and connections
-
Collapsible/Expandable: JavaScript-enabled expand/collapse functionality for complex schemas (optional)
-
Clickable Elements: Navigate between related schema components
-
Documentation Display: Inline display of
<xs:documentation>annotations -
Color-coded Symbols: Different visual styles for elements, attributes, compositors, and constraints
Flexible output options
-
Single Diagram: Generate one SVG showing the entire schema or a specific root element
-
Per-Element Diagrams: Generate separate SVG files for each top-level element
-
Custom Styling: Embed CSS in SVG files or use external stylesheets
-
Output Directory: Organize generated diagrams in custom folder structures
Processing capabilities
-
Multiple XSD Files: Process multiple schema files in a single run
-
Loop Detection: Automatically detects and marks recursive element references
-
Namespace Handling: Properly displays and distinguishes multiple namespaces
-
Documentation Extraction: Extracts and formats XSD documentation for display
-
Large Schema Support: Handles complex, real-world schemas with hundreds of elements
-
Type Resolution: Comprehensive resolution of named types, group references, attribute groups, element references, and type inheritance (extension/restriction)
Installation
Add this line to your application’s Gemfile:
gem 'xsdvi'
And then execute:
bundle install
Or install it yourself as:
gem install xsdvi
Usage
Basic usage
Generate an SVG diagram from an XSD file:
xsdvi generate schema.xsd
This creates schema.svg in the current directory, showing all top-level
elements and their hierarchical structure.
Generate diagram for specific root element
xsdvi generate schema.xsd -r UnitsML
This generates a diagram starting from the UnitsML element, showing its
complete structure and all nested elements.
Generate separate SVG for each element
xsdvi generate schema.xsd -r all -o -p output/diagrams
This creates individual SVG files for each top-level element in the
output/diagrams directory.
Command-line options
-r, --root-node-name NAME-
Specify the schema root element name to visualize. Use "all" to generate diagrams for all top-level elements. If omitted, generates a complete schema diagram showing all elements.
-o, --one-node-only-
Generate diagram showing only the specified element without its children (single-level view). Automatically enabled when using
-r all. This mode hides the expand/collapse control buttons. -p, --output-path PATH-
Specify output directory for generated SVG files. The tool automatically creates the directory if it doesn’t exist. If omitted, files are created in the current directory.
--embody-style-
Embed CSS styling directly in each SVG file (default: true). This creates self-contained SVG files that display correctly without external dependencies.
--generate-style FILE-
Generate an external CSS file with the specified name and reference it from SVG files. Useful when generating multiple diagrams that should share styling.
--use-style URL-
Reference an existing external CSS file at the specified URL in generated SVG files. The CSS file must be accessible when the SVG is viewed.
Examples
xsdvi generate UnitsML-v1.0.xsd -r UnitsML
Creates UnitsML.svg showing the complete structure starting from the UnitsML
root element, including all nested elements, attributes, and constraints.
xsdvi generate UnitsML-v1.0.xsd -r Quantity -o
Creates Quantity.svg showing only the Quantity element definition without
expanding its children, useful for focused documentation.
xsdvi generate UnitsML-v1.0.xsd -r all -o -p images/SVG
Creates individual SVG files (Quantity.svg, Unit.svg, etc.) in the
images/SVG directory, one for each top-level element in the schema.
xsdvi generate schema.xsd --generate-style custom.css -p output
Creates output/schema.svg and output/custom.css, with the SVG referencing
the external stylesheet. Useful for customizing diagram appearance.
Comparing Java and Ruby outputs
The compare command generates side-by-side visual comparisons of Java XsdVi
v1.3 and Ruby XsdVi outputs. This is useful for validation, documentation, and
visual quality assurance.
Basic comparison
xsdvi compare schema.xsd
This command:
-
Automatically downloads Java XsdVi JAR v1.3 (first run only, cached in
~/.xsdvi/) -
Generates SVGs using both Java and Ruby implementations
-
Extracts metadata and symbol counts from both outputs
-
Creates an interactive HTML comparison page in
comparisons/<schema>-<timestamp>/
Comparison command options
-r, --root-node-name NAME-
Specify the schema root element to visualize (same as
generatecommand). Use "all" to generate comparisons for all top-level elements. -o, --output-path PATH-
Specify output directory for comparison files. Default:
comparisons/<schema>-<timestamp>/ --skip-java-
Skip Java generation and use existing Java outputs. Useful when re-running comparison with updated Ruby code.
--skip-ruby-
Skip Ruby generation and use existing Ruby outputs. Useful for comparing against manually generated Ruby outputs.
-O, --open-
Automatically open the comparison HTML in your default browser after generation.
Comparison examples
xsdvi compare UnitsML-v1.0.xsd -r UnitsML
Downloads Java XsdVi (if needed), generates SVGs with both implementations, and creates an interactive comparison showing:
-
Side-by-side SVG iframes
-
Statistics table comparing file sizes and symbol counts
-
Generation time for each implementation
-
Visual diff highlighting any differences
xsdvi compare schema.xsd -r all -O
Generates comparison for all top-level elements and automatically opens the comparison page in your browser. The HTML includes an element selector dropdown to switch between different element diagrams.
xsdvi compare schema.xsd --skip-java -o my-comparison
Reuses existing Java output and generates fresh Ruby output, useful during Ruby development to quickly validate changes without regenerating Java outputs.
Comparison output structure
After running a comparison, you get:
comparisons/<schema>-<timestamp>/
├── java/
│ └── <schema>.svg # Java XsdVi output
├── ruby/
│ └── <schema>.svg # Ruby XsdVi output
└── comparison.html # Interactive comparison page
The comparison HTML displays:
-
Statistics table: File counts, sizes, symbol counts by type, generation times
-
Visual indicators: Green checkmarks for matches, red X for mismatches
-
Split-screen view: Java output (left) and Ruby output (right) in synchronized iframes
-
Element selector: Dropdown to switch between elements (when using
-r all)
Architecture
Core components
- TreeElement and TreeBuilder
-
Manage the tree structure representing the XSD schema hierarchy. TreeElement provides parent-child relationships, traversal methods, and unique node identification.
- XsdHandler
-
Parses XSD files using Nokogiri and builds the symbol tree structure. Handles all XSD constructs including elements, attributes, compositors, wildcards, and identity constraints. Performs loop detection for recursive schemas.
- Symbol classes
-
14 specialized symbol types representing different XSD constructs:
-
Element, Attribute - schema components
-
Choice, Sequence, All - compositors
-
Any, AnyAttribute - wildcards
-
Key, Keyref, Unique - identity constraints
-
Selector, Field - constraint components
-
Loop - recursive reference indicator
-
Schema - root container
-
- SVG::Generator
-
Generates SVG output files from the symbol tree. Manages layout, styling, resource loading, and recursive symbol drawing. Produces interactive diagrams with JavaScript-enabled expand/collapse functionality.
- CLI
-
Thor-based command-line interface providing easy access to all generation options with built-in help and validation.
Data flow
XSD File → XsdHandler (Nokogiri parsing)
↓
Type Registry Collection (complexTypes, simpleTypes, groups, etc.)
↓
Symbol Tree Building (TreeBuilder with on-demand type resolution)
↓
Symbol Processing (prepare_box, calculate dimensions)
↓
SVG Generation (recursive drawing)
↓
SVG File Output
Type resolution architecture
The type resolution system is the core feature that enables XSDVI to correctly process all XSD schema structures. It resolves references to named types, groups, and elements, and handles type inheritance patterns.
Resolution mechanisms
The system implements six resolution mechanisms:
Type Registry System
All named type and group definitions are collected during schema parsing into hash-based registries for O(1) lookup performance:
-
Complex types (
<xs:complexType name="…">) -
Simple types (
<xs:simpleType name="…">) -
Groups (
<xs:group name="…">) -
Attribute groups (
<xs:attributeGroup name="…">) -
Global elements (
<xs:element name="…">)
Named Type Resolution
Resolves type references in element declarations:
<xs:element name="Foo" type="BarType"/>
The system:
-
Strips namespace prefixes from type names
-
Checks if the type is a W3C builtin (string, boolean, decimal, etc.)
-
Looks up custom types in the complex/simple type registries
-
Processes the resolved type definition inline
Group Reference Resolution
Resolves group references in compositors:
<xs:sequence>
<xs:group ref="CommonElements"/>
</xs:sequence>
The referenced group’s contents are expanded inline, maintaining proper tree hierarchy.
Attribute Group Reference Resolution
Resolves attribute group references:
<xs:complexType>
<xs:attributeGroup ref="CommonAttributes"/>
</xs:complexType>
Supports nested attribute group references (recursive resolution).
Element Reference Resolution
Resolves element references with proper cardinality:
<xs:element ref="CommonElement" minOccurs="0" maxOccurs="unbounded"/>
The system preserves cardinality constraints while resolving to the element definition.
Extension and Restriction Processing
Handles type inheritance via complexContent and simpleContent:
<xs:complexType name="ExtendedType">
<xs:complexContent>
<xs:extension base="BaseType">
<xs:sequence>
<!-- Additional elements -->
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
The system:
-
Resolves the base type from registries
-
Processes base type structure first (inherited elements/attributes)
-
Processes extension-specific additions
-
Maintains single inheritance hierarchy
Performance characteristics
-
Registry collection: O(n) where n = total XSD nodes
-
Type lookup: O(1) hash access
-
Recursive resolution: Bounded by schema depth
-
Loop detection prevents infinite recursion
-
On-demand resolution (lazy evaluation)
This architecture enables XSDVI to correctly process 100% of XSD schema structures, matching the Java version’s capabilities.
Differences from Java version
Text wrapping
The Ruby implementation uses a custom text wrapping algorithm that produces
slightly different line breaks compared to Apache Commons Text
WordUtils.wrap(). The Ruby version optimizes for keeping related content
together when it fits within the line width, while maintaining valid XML
structure.
Impact: Documentation text may wrap at different character positions, but the visual output and functionality remain identical. The Ruby version ensures HTML tags and entities in documentation are not broken across lines, improving SVG validity.
Example:
Java may break HTML tags mid-structure:
<text>See <a</text>
<text>href="...">text</a>.</text>
Ruby keeps tags together for better validity:
<text>See <a href="...">text</a>.</text>
Both outputs are functionally equivalent and render correctly in SVG viewers. The Ruby approach actually produces more semantically correct output by preserving the integrity of embedded HTML elements.
Development
After checking out the repo, run bundle install to install dependencies.
Run tests:
bundle exec rspec
Run rubocop:
bundle exec rubocop
Testing
Test suite overview
The XSDVI Ruby test suite provides comprehensive validation of all functionality with 46 tests across two categories:
-
Integration tests (14 tests): Validate complete end-to-end processing and compare outputs with Java version
-
Type resolution tests (32 tests): Validate all 7 type resolution mechanisms
Running tests
Run all tests
bundle exec rspec
Run specific test file
# Type resolution tests only
bundle exec rspec spec/xsdvi/type_resolution_spec.rb
# Output comparison tests only
bundle exec rspec spec/xsdvi/output_comparison_spec.rb
Run with documentation format
bundle exec rspec --format documentation
Run specific test group
bundle exec rspec spec/xsdvi/type_resolution_spec.rb -e "Named Type Resolution"
Type resolution test coverage
The type resolution test suite validates that XSDVI Ruby correctly processes all XSD schema constructs:
7 Resolution Mechanisms Tested:
-
Named Type Resolution - Complex and simple type references
-
Group Reference Resolution - Model group (
<xs:group ref="…">) expansion -
Attribute Group Reference Resolution - Attribute group references including nested
-
Element Reference Resolution - Global element references with cardinality
-
Extension/Restriction Resolution - Type inheritance via
complexContent/simpleContent -
Identity Constraint Resolution - Key, keyref, unique, selector, field
-
Circular Reference Resolution - Self-referencing and mutual circular references
Test Fixtures:
The test suite uses 7 XSD fixture files in spec/fixtures/type_resolution/:
-
named_types.xsd- Type reference resolution -
group_refs.xsd- Group reference expansion -
attribute_groups.xsd- Attribute group references -
element_refs.xsd- Element references with cardinality -
inheritance.xsd- Extension and restriction -
identity_constraints.xsd- Key/keyref/unique constraints -
circular.xsd- Circular reference handling
For detailed test documentation, see spec/xsdvi/TYPE_RESOLUTION_TESTING.md.
Generating comparison files
To validate that XSDVI Ruby produces output identical to the Java version:
Generate Ruby output for UnitsML schema
xsdvi generate spec/fixtures/UnitsML-v1.0-csd04.xsd -r UnitsML \
-p spec/fixtures/ruby
Generate Ruby output for all elements
xsdvi generate spec/fixtures/UnitsML-v1.0-csd04.xsd -r all -o \
-p spec/fixtures/ruby/all
Generate Ruby output for TestXMLEntities
xsdvi generate spec/fixtures/TestXMLEntities.xsd \
-p spec/fixtures/ruby
Compare with Java version
The test suite automatically compares Ruby outputs with Java reference outputs in spec/fixtures/java/. The comparison normalizes known differences (text wrapping) and validates structural equivalence:
bundle exec rspec spec/xsdvi/output_comparison_spec.rb
Manual comparison
You can manually compare outputs using diff tools:
# Compare UnitsML outputs
diff spec/fixtures/java/UnitsML-v1.0-csd04.svg \
spec/fixtures/ruby/UnitsML-v1.0-csd04.svg
# Compare specific element
diff spec/fixtures/java/all/Quantity.svg \
spec/fixtures/ruby/all/Quantity.svg
Note: Direct diff will show text wrapping differences. Use the RSpec comparison tests for structural validation.
Generating fresh Java comparison files
If you need to regenerate Java version outputs (requires Java XsdVi):
# Assuming Java xsdvi.jar is available
java -jar xsdvi.jar --in spec/fixtures/UnitsML-v1.0-csd04.xsd \
--root UnitsML --out spec/fixtures/java/UnitsML-v1.0-csd04.svg
# For all elements
java -jar xsdvi.jar --in spec/fixtures/UnitsML-v1.0-csd04.xsd \
--root all --one-node-only --out spec/fixtures/java/all
Test validation approach
The test suite uses Canon gem for XML-aware comparison that:
-
Normalizes whitespace differences
-
Handles attribute ordering variations
-
Validates structural equivalence
-
Ignores text wrapping differences in
data-desc-*attributes -
Focuses on semantic correctness rather than byte-identical output
This ensures that XSDVI Ruby produces functionally identical diagrams to the Java version while allowing minor presentational differences.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/metanorma/xsdvi-ruby
Copyright and license
Copyright Ribose Inc.
The gem is available as open source under the Ribose 3-Clause BSD License.
Acknowledgments
This is a Ruby port of the Metanorma version of the XsdVi tool, which is itself a fork of the original XsdVi Java tool created by Václav Slavìtínský. See the original project at https://sourceforge.net/projects/xsdvi/
The Ruby port maintains compatibility with the Java version’s output format while providing a pure Ruby implementation suitable for modern Ruby applications and workflows.