validate_xml_xsi 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/README.md +4 -4
- data/bin/validate_xml_xsi +3 -1
- data/lib/validate_xml_xsi.rb +96 -34
- data/validate_xml_xsi.gemspec +2 -1
- metadata +17 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cda44bf92902eaf418da297019b5390015d244cdb20da04142b01036146a54ff
|
|
4
|
+
data.tar.gz: c9ac5ba79dd64afc4d47cab9d8de4379e742db5008342f8888dcec63ebac5900
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e5db59f5fbf7c3aaf61c931b0ea5227c9d08aaba1e65de797721f06c73b58939766f4390beac7ef919784d55b51abc4a4b21416f1edafdbe20b9ffec090565ff
|
|
7
|
+
data.tar.gz: a2217e8efdfb6b6ce53dec4fb7f9ec09147dd95fdc2a241a3ae277b7f3155867c33129780f7b8766b0039422fa76d2440346a72382662cec98cd1b43ae51ce2a
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
## v0.3.0
|
|
2
|
+
|
|
3
|
+
* Change overall structure to make XML_XSI::Schema a normal class (not just a singleton)
|
|
4
|
+
* Allow the Schema to return the xsd document string (so it can be used externally to build a XSD document tree)
|
|
5
|
+
|
|
1
6
|
## v0.2.4
|
|
2
7
|
|
|
3
8
|
* Fix some silly syntax errors
|
data/README.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# validate_xml_xsi
|
|
2
2
|
|
|
3
|
-
This gem validates XML against it's
|
|
3
|
+
This gem validates XML against it's XSD that is created from xsi:schemaLocation elements.
|
|
4
4
|
|
|
5
|
-
It does this by first parsing the XML and searching for xsi:schemaLocation
|
|
5
|
+
It does this by first parsing the XML and searching for elements that include xsi:schemaLocation attributes. It then creates an XSD schema document that includes the root namespace and further imports additional namespaces discovered in the XML.
|
|
6
6
|
|
|
7
|
-
It then validates the document against that schema and outputs any error messages.
|
|
7
|
+
It then validates the document against that constructed schema document and outputs any error messages.
|
|
8
8
|
|
|
9
|
-
Note: this gem utilizes the 'nokogiri' gem for XML parsing and schema validation.
|
|
9
|
+
Note: this gem utilizes the 'nokogiri' gem (which in turn relies on Gnome's libxml2) for the XML parsing and schema validation.
|
|
10
10
|
|
|
11
11
|
## Installation
|
|
12
12
|
|
data/bin/validate_xml_xsi
CHANGED
|
@@ -3,7 +3,9 @@ require 'validate_xml_xsi'
|
|
|
3
3
|
|
|
4
4
|
while ARGV.size > 0
|
|
5
5
|
fname = ARGV.shift
|
|
6
|
-
|
|
6
|
+
xml_doc = XML_XSI::parse(fname)
|
|
7
|
+
xsd = XML_XSI::Schema.new(xml_doc)
|
|
8
|
+
errors = xsd.validate
|
|
7
9
|
if errors.empty?
|
|
8
10
|
puts "XML Schema check complete - NO ERRORS!"
|
|
9
11
|
else
|
data/lib/validate_xml_xsi.rb
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
require 'nokogiri'
|
|
3
3
|
|
|
4
|
+
## Monkey-patch Nokogiri::XML::SyntaxError::aggregate to also keep the ENTIRE LIST of errors - ARGH!
|
|
5
|
+
Nokogiri::XML::SyntaxError.class_eval do
|
|
6
|
+
class << self
|
|
7
|
+
alias_method :orig_aggregate, :aggregate
|
|
8
|
+
def aggregate(errors)
|
|
9
|
+
agg_err = orig_aggregate(errors)
|
|
10
|
+
agg_err.instance_variable_set(:@aggregate, errors)
|
|
11
|
+
agg_err.define_singleton_method(:aggregate) { instance_variable_get(:@aggregate) }
|
|
12
|
+
agg_err
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
4
17
|
class XML_XSI
|
|
5
18
|
def self.parse(obj)
|
|
6
19
|
filename = nil
|
|
@@ -11,8 +24,49 @@ class XML_XSI
|
|
|
11
24
|
xml_doc
|
|
12
25
|
end
|
|
13
26
|
|
|
27
|
+
def self.find_schema_locations(xml_doc)
|
|
28
|
+
## Include the default xml namespace
|
|
29
|
+
schema_locations = {}
|
|
30
|
+
|
|
31
|
+
## Determine if the document has reference to the namespace "http://www.w3.org/2001/XMLSchema-instance"
|
|
32
|
+
## which is used for defining schemaLocations
|
|
33
|
+
xsi_prefix = xml_doc.namespaces.invert['http://www.w3.org/2001/XMLSchema-instance']&.delete_prefix('xmlns:')
|
|
34
|
+
|
|
35
|
+
## Iterate over all the elements and find any xsi:schemaLocation attributes
|
|
36
|
+
## and build a hash of all of the namespaces and locations
|
|
37
|
+
unless xsi_prefix.nil?
|
|
38
|
+
xsi_loc = "#{xsi_prefix}:schemaLocation"
|
|
39
|
+
xml_doc.search("//*[@#{xsi_loc}]").each do |elem|
|
|
40
|
+
elem[xsi_loc].scan(/(\S+)\s+(\S+)/).each do |ns_set|
|
|
41
|
+
if ns_loc = schema_locations[ns_set.first]
|
|
42
|
+
unless ns_loc.eql?(ns_set.last)
|
|
43
|
+
raise DocumentError.new("MISMATCHING namespace: #{ns_set.first} -> #{ns_loc} VS #{ns_set.last}")
|
|
44
|
+
end
|
|
45
|
+
else
|
|
46
|
+
schema_locations[ns_set.first] = ns_set.last
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
schema_locations
|
|
52
|
+
end
|
|
53
|
+
|
|
14
54
|
class Schema
|
|
15
55
|
class DocumentError < StandardError; end
|
|
56
|
+
class NamespaceError < StandardError
|
|
57
|
+
def initialize
|
|
58
|
+
super("Unable to determine default/target (xmlns) namespace!")
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
class LocationError < StandardError
|
|
62
|
+
def initialize(ns_href = nil)
|
|
63
|
+
msg = ns_href.nil? ?
|
|
64
|
+
"No schema locations defined or provided" :
|
|
65
|
+
"No location for the default (xmlns) namespace schema: #{ns_href}"
|
|
66
|
+
super(msg)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
## Unified Error reportign class for both XSD and XML errors
|
|
16
70
|
class ValidationError < StandardError
|
|
17
71
|
attr_reader :type, :file, :line, :column, :level, :message, :description, :error
|
|
18
72
|
def initialize(type, err, filename = nil)
|
|
@@ -34,60 +88,68 @@ class XML_XSI
|
|
|
34
88
|
end
|
|
35
89
|
end
|
|
36
90
|
|
|
91
|
+
class Erroneous
|
|
92
|
+
attr_reader :errors
|
|
93
|
+
def initialize(ex)
|
|
94
|
+
errs = ex.respond_to?(:aggregate) ? ex.aggregate : [ex]
|
|
95
|
+
@errors = errs.map { |err| ValidationError.new(:XSD, err) }
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
37
99
|
attr_reader :xsd
|
|
38
|
-
def initialize(xml_doc)
|
|
100
|
+
def initialize(xml_doc, schema_locations = {})
|
|
101
|
+
raise ArgumentError.new("Provided schema locations must be a Hash") unless schema_locations.is_a?(Hash)
|
|
39
102
|
unless xml_doc.is_a?(Nokogiri::XML::Document)
|
|
40
103
|
raise DocumentError.new("invalid Nokogiri::XML::Document - #{xml_doc.class.name}")
|
|
41
104
|
end
|
|
42
105
|
@document = xml_doc
|
|
43
106
|
## Determine default/top/root namespace
|
|
44
|
-
|
|
107
|
+
@ns_href = nil
|
|
45
108
|
@document.namespaces.each do |ns_prefix, ns_href|
|
|
46
|
-
|
|
109
|
+
@ns_href = ns_href if ns_prefix.nil? || ns_prefix.empty? || ns_prefix.eql?('xmlns')
|
|
47
110
|
end
|
|
48
|
-
raise
|
|
111
|
+
raise NamespaceError.new if @ns_href.nil? || @ns_href.empty?
|
|
112
|
+
|
|
113
|
+
## Determine schema locations found in the source document
|
|
114
|
+
schema_locations.merge!(XML_XSI::find_schema_locations(@document))
|
|
115
|
+
|
|
116
|
+
raise LocationError.new if schema_locations.empty?
|
|
117
|
+
raise LocationError.new(@ns_href) unless schema_locations.include?(@ns_href)
|
|
118
|
+
|
|
49
119
|
## Build an all-in-one XSD document that imports all of the separate schema locations
|
|
50
120
|
@xsd = "<?xml version=\"1.0\"?>\n"
|
|
51
121
|
@xsd << "<xsd:schema xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"\n" \
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
raise DocumentError.new("MISMATCHING namespace: #{ns_set.first} -> #{ns_loc} VS #{ns_set.last}")
|
|
63
|
-
end
|
|
64
|
-
else
|
|
65
|
-
schemata_by_ns[ns_set.first] = ns_set.last
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
schemata_by_ns.each do |ns_href, ns_file|
|
|
70
|
-
@xsd << (ns_href.eql?(target_ns_href) ?
|
|
71
|
-
" <xsd:include schemaLocation=\"#{ns_file}\"/>\n" :
|
|
72
|
-
" <xsd:import namespace=\"#{ns_href}\" schemaLocation=\"#{ns_file}\"/>\n")
|
|
122
|
+
" targetNamespace=\"#{@ns_href}\"\n" \
|
|
123
|
+
" version=\"1.0\">\n"
|
|
124
|
+
|
|
125
|
+
## Minimally we need the target namespace location or we have nothing to include
|
|
126
|
+
@xsi_file = schema_locations.delete(@ns_href)
|
|
127
|
+
@xsd << " <xsd:include schemaLocation=\"#{@xsi_file}\"/>\n" unless @xsi_file.nil?
|
|
128
|
+
|
|
129
|
+
## Now add imports for the other defined schemaLocations
|
|
130
|
+
schema_locations.each do |ns_href, ns_file|
|
|
131
|
+
@xsd << " <xsd:import namespace=\"#{ns_href}\" schemaLocation=\"#{ns_file}\"/>\n"
|
|
73
132
|
end
|
|
74
133
|
@xsd << "</xsd:schema>\n"
|
|
75
134
|
|
|
76
135
|
## Create the Schema objects
|
|
77
|
-
|
|
136
|
+
begin
|
|
137
|
+
@schema = Nokogiri::XML::Schema.new(@xsd)
|
|
138
|
+
rescue Nokogiri::XML::SyntaxError => ex
|
|
139
|
+
## Trap the errors so we can finish initialization and then
|
|
140
|
+
## report the errors in a sane manner
|
|
141
|
+
@schema = Erroneous.new(ex)
|
|
142
|
+
end
|
|
78
143
|
end
|
|
79
144
|
|
|
145
|
+
def errors; @schema.errors; end
|
|
146
|
+
|
|
80
147
|
def validate
|
|
81
|
-
errors = []
|
|
82
|
-
@schema.errors.each do |err|
|
|
83
|
-
errors << ValidationError.new(:XSD, err)
|
|
84
|
-
end
|
|
85
148
|
errs = @schema.validate(@document)
|
|
86
|
-
errs.
|
|
87
|
-
fname =
|
|
88
|
-
|
|
149
|
+
errs.map do |err|
|
|
150
|
+
fname = err.file.nil? ? @document.filename : err.file
|
|
151
|
+
ValidationError.new(:XML, err, fname)
|
|
89
152
|
end
|
|
90
|
-
errors
|
|
91
153
|
end
|
|
92
154
|
end
|
|
93
155
|
end
|
data/validate_xml_xsi.gemspec
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Gem::Specification.new do |spec|
|
|
2
2
|
spec.name = "validate_xml_xsi"
|
|
3
|
-
spec.version = "0.
|
|
3
|
+
spec.version = "0.5.0"
|
|
4
4
|
spec.authors = ["David Hansen"]
|
|
5
5
|
spec.email = ["david@hansen4.net"]
|
|
6
6
|
|
|
@@ -26,4 +26,5 @@ Gem::Specification.new do |spec|
|
|
|
26
26
|
spec.add_development_dependency "rake", ">= 13.0"
|
|
27
27
|
|
|
28
28
|
spec.add_dependency "nokogiri", ">= 1.13.2"
|
|
29
|
+
spec.add_dependency "libxml-ruby", ">= 5.0.0"
|
|
29
30
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: validate_xml_xsi
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- David Hansen
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: bundler
|
|
@@ -52,7 +51,20 @@ dependencies:
|
|
|
52
51
|
- - ">="
|
|
53
52
|
- !ruby/object:Gem::Version
|
|
54
53
|
version: 1.13.2
|
|
55
|
-
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: libxml-ruby
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - ">="
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: 5.0.0
|
|
61
|
+
type: :runtime
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: 5.0.0
|
|
56
68
|
email:
|
|
57
69
|
- david@hansen4.net
|
|
58
70
|
executables:
|
|
@@ -76,7 +88,6 @@ metadata:
|
|
|
76
88
|
homepage_uri: https://github.com/d-hansen/validate_xml_xsi
|
|
77
89
|
source_code_uri: https://github.com/d-hansen/validate_xml_xsi
|
|
78
90
|
changelog_uri: https://github.com/d-hansen/validate_xml_xsi/blob/master/CHANGELOG.md
|
|
79
|
-
post_install_message:
|
|
80
91
|
rdoc_options: []
|
|
81
92
|
require_paths:
|
|
82
93
|
- lib
|
|
@@ -91,8 +102,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
91
102
|
- !ruby/object:Gem::Version
|
|
92
103
|
version: '0'
|
|
93
104
|
requirements: []
|
|
94
|
-
rubygems_version:
|
|
95
|
-
signing_key:
|
|
105
|
+
rubygems_version: 4.0.2
|
|
96
106
|
specification_version: 4
|
|
97
107
|
summary: Validate XML against it's embedded XSI elements that define the XSD's.
|
|
98
108
|
test_files: []
|