validate_xml_xsi 0.4.0 → 0.5.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dbe79de11a54ebfff237869fbef6f4ccb9ce8b063dc3305299cb1fcb7856298b
4
- data.tar.gz: 549031f8aa72adca7434f9294bfef40e2b2ce70b8980b04960cd20ab320dace4
3
+ metadata.gz: be0377beafcedfc0cb8ad1b50bba9ccd0a7ea7791cddbd83557d45b0b5d16686
4
+ data.tar.gz: 2d92abfa231c9c62456ca12e5229fd1a772c26c4be40e22de6b4be5c22262cde
5
5
  SHA512:
6
- metadata.gz: 1336127c5245a94b7a8915f5d87372e17ab52bad43c32d1c07ef165d26db0ca1182baefcc826bd58cd401e213a2f7c04129dfea28d7c950d9fe1e06e2bb6780a
7
- data.tar.gz: b60f8dcc6904c9b706f338a7afa7438fd98b7029a2a06b9f07010a63e3cddd1f0913bad0bc5ec2d608c3446c137339bb2ae8640251e7b4775ac006b9799e0c75
6
+ metadata.gz: f9debac28eff1eedd8684f49da15528d48e79018b5a505d2e89fbc1c9f1c949fb119abb9c1c25d6d3f376cc2506d9f88e2fdb380fce49f844564cac5e5aad26e
7
+ data.tar.gz: 32de91751a24a6c271236bf56091d1ba58d5b69b1df6ae840accdc5d1c6e7aad8dd0dfc43860b38f670020cde5d39ac08a2ae0f3f70ece3616c08be453ab57d3
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
- errors = XML_XSI::Schema::new(XML_XSI.parse(fname)).validate
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
@@ -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,51 @@ 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 Schema::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
+ attr_reader :ns_href
63
+ def initialize(ns_href = nil)
64
+ @ns_href = ns_href
65
+ msg = ns_href.nil? ?
66
+ "No schema locations defined or provided" :
67
+ "No location for the default (xmlns) namespace schema: #{ns_href}"
68
+ super(msg)
69
+ end
70
+ end
71
+ ## Unified Error reportign class for both XSD and XML errors
16
72
  class ValidationError < StandardError
17
73
  attr_reader :type, :file, :line, :column, :level, :message, :description, :error
18
74
  def initialize(type, err, filename = nil)
@@ -34,50 +90,43 @@ class XML_XSI
34
90
  end
35
91
  end
36
92
 
93
+ class Erroneous
94
+ attr_reader :errors
95
+ def initialize(ex)
96
+ errs = ex.respond_to?(:aggregate) ? ex.aggregate : [ex]
97
+ @errors = errs.map { |err| ValidationError.new(:XSD, err) }
98
+ end
99
+ end
100
+
37
101
  attr_reader :xsd
38
- def initialize(xml_doc, parent_xml_doc = nil)
102
+ def initialize(xml_doc, schema_locations = {})
103
+ raise ArgumentError.new("Provided schema locations must be a Hash") unless schema_locations.is_a?(Hash)
39
104
  unless xml_doc.is_a?(Nokogiri::XML::Document)
40
105
  raise DocumentError.new("invalid Nokogiri::XML::Document - #{xml_doc.class.name}")
41
106
  end
42
- unless parent_xml_doc.nil? || parent_xml_doc.is_a?(Nokogiri::XML::Document)
43
- raise DocumentError.new("invalid parent Nokogiri::XML::Document - #{parent_xml_doc.class.name}")
44
- end
45
107
  @document = xml_doc
46
108
  ## Determine default/top/root namespace
47
- target_ns_href = nil
109
+ @ns_href = nil
48
110
  @document.namespaces.each do |ns_prefix, ns_href|
49
- target_ns_href = ns_href if ns_prefix.nil? || ns_prefix.empty? || ns_prefix.eql?('xmlns')
50
- end
51
- if target_ns_href.nil? || target_ns_href.empty?
52
- raise DocumentError.new("Unable to determine a default (xmlns) namespace!")
111
+ @ns_href = ns_href if ns_prefix.nil? || ns_prefix.empty? || ns_prefix.eql?('xmlns')
53
112
  end
113
+ raise NamespaceError.new if @ns_href.nil? || @ns_href.empty?
54
114
 
55
- ## Determine schema locations, optionally inheriting their location declarations from a parent document
56
- schema_locations = parent_xml_doc.nil? ? {} : self.class.find_schema_locations(parent_xml_doc)
57
- schema_locations.merge!(self.class.find_schema_locations(@document))
115
+ ## Determine schema locations found in the source document
116
+ schema_locations.merge!(XML_XSI::find_schema_locations(@document))
58
117
 
59
- ## If we still don't have a file location for the target namespace, attempt to look for
60
- ## one based on the name of the root node (assuming that where the namespace was declared).
61
- if !schema_locations.include?(target_ns_href) &&
62
- @document.root.namespace.href.eql?(target_ns_href)
63
- root_file_xsd = "#{@document.root.name}.xsd"
64
- schema_locations[target_ns_href] = root_file_xsd if File.exist?(root_file_xsd)
65
- end
66
-
67
- unless schema_locations.include?(target_ns_href)
68
- ## XXX - Another possibility would be to default to a file named after the node name declaring the xmlns
69
- raise DocumentError.new("Unable to locate a source/file for the default (xmlns) namespace schema!")
70
- end
118
+ raise LocationError.new if schema_locations.empty?
119
+ raise LocationError.new(@ns_href) unless schema_locations.include?(@ns_href)
71
120
 
72
121
  ## Build an all-in-one XSD document that imports all of the separate schema locations
73
122
  @xsd = "<?xml version=\"1.0\"?>\n"
74
123
  @xsd << "<xsd:schema xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"\n" \
75
- " targetNamespace=\"#{target_ns_href}\"\n" \
76
- " version=\"1.0\">\n"
124
+ " targetNamespace=\"#{@ns_href}\"\n" \
125
+ " version=\"1.0\">\n"
77
126
 
78
127
  ## Minimally we need the target namespace location or we have nothing to include
79
- target_ns_file = schema_locations.delete(target_ns_href)
80
- @xsd << " <xsd:include schemaLocation=\"#{target_ns_file}\"/>\n" unless target_ns_file.nil?
128
+ @xsi_file = schema_locations.delete(@ns_href)
129
+ @xsd << " <xsd:include schemaLocation=\"#{@xsi_file}\"/>\n" unless @xsi_file.nil?
81
130
 
82
131
  ## Now add imports for the other defined schemaLocations
83
132
  schema_locations.each do |ns_href, ns_file|
@@ -86,47 +135,23 @@ class XML_XSI
86
135
  @xsd << "</xsd:schema>\n"
87
136
 
88
137
  ## Create the Schema objects
89
- @schema = Nokogiri::XML::Schema.new(@xsd)
138
+ begin
139
+ @schema = Nokogiri::XML::Schema.new(@xsd)
140
+ rescue Nokogiri::XML::SyntaxError => ex
141
+ ## Trap the errors so we can finish initialization and then
142
+ ## report the errors in a sane manner
143
+ @schema = Erroneous.new(ex)
144
+ end
90
145
  end
91
146
 
147
+ def errors; @schema.errors; end
148
+
92
149
  def validate
93
- errors = []
94
- @schema.errors.each do |err|
95
- errors << ValidationError.new(:XSD, err)
96
- end
97
150
  errs = @schema.validate(@document)
98
- errs.each do |err|
99
- fname = (err.file.nil?) ? @document.filename : err.file
100
- errors << ValidationError.new(:XML, err, fname)
101
- end
102
- errors
103
- end
104
-
105
- def self.find_schema_locations(xml_doc)
106
- ## Include the default xml namespace
107
- schema_locations = {}
108
-
109
- ## Determine if the document has reference to the namespace "http://www.w3.org/2001/XMLSchema-instance"
110
- ## which is used for defining schemaLocations
111
- xsi_prefix = xml_doc.namespaces.invert['http://www.w3.org/2001/XMLSchema-instance']&.delete_prefix('xmlns:')
112
-
113
- ## Iterate over all the elements and find any xsi:schemaLocation attributes
114
- ## and build a hash of all of the namespaces and locations
115
- unless xsi_prefix.nil?
116
- xsi_loc = "#{xsi_prefix}:schemaLocation"
117
- xml_doc.search("//*[@#{xsi_loc}]").each do |elem|
118
- elem[xsi_loc].scan(/(\S+)\s+(\S+)/).each do |ns_set|
119
- if ns_loc = schema_locations[ns_set.first]
120
- unless ns_loc.eql?(ns_set.last)
121
- raise DocumentError.new("MISMATCHING namespace: #{ns_set.first} -> #{ns_loc} VS #{ns_set.last}")
122
- end
123
- else
124
- schema_locations[ns_set.first] = ns_set.last
125
- end
126
- end
127
- end
151
+ errs.map do |err|
152
+ fname = err.file.nil? ? @document.filename : err.file
153
+ ValidationError.new(:XML, err, fname)
128
154
  end
129
- schema_locations
130
155
  end
131
156
  end
132
157
  end
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |spec|
2
2
  spec.name = "validate_xml_xsi"
3
- spec.version = "0.4.0"
3
+ spec.version = "0.5.1"
4
4
  spec.authors = ["David Hansen"]
5
5
  spec.email = ["david@hansen4.net"]
6
6
 
@@ -25,5 +25,6 @@ Gem::Specification.new do |spec|
25
25
  spec.add_development_dependency "bundler", ">= 2.1"
26
26
  spec.add_development_dependency "rake", ">= 13.0"
27
27
 
28
- spec.add_dependency "nokogiri", ">= 1.13.2"
28
+ spec.add_dependency "nokogiri", ">= 1.18.0"
29
+ spec.add_dependency "libxml-ruby", ">= 5.0.0"
29
30
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: validate_xml_xsi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Hansen
@@ -43,14 +43,28 @@ dependencies:
43
43
  requirements:
44
44
  - - ">="
45
45
  - !ruby/object:Gem::Version
46
- version: 1.13.2
46
+ version: 1.18.0
47
47
  type: :runtime
48
48
  prerelease: false
49
49
  version_requirements: !ruby/object:Gem::Requirement
50
50
  requirements:
51
51
  - - ">="
52
52
  - !ruby/object:Gem::Version
53
- version: 1.13.2
53
+ version: 1.18.0
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
54
68
  email:
55
69
  - david@hansen4.net
56
70
  executables: