xsd 1.0.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 +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +124 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +6 -0
- data/LICENSE +21 -0
- data/Makefile +7 -0
- data/README.md +68 -0
- data/Rakefile +25 -0
- data/lib/xsd/base_object.rb +347 -0
- data/lib/xsd/exceptions.rb +12 -0
- data/lib/xsd/generator.rb +137 -0
- data/lib/xsd/objects/all.rb +22 -0
- data/lib/xsd/objects/annotation.rb +19 -0
- data/lib/xsd/objects/any.rb +31 -0
- data/lib/xsd/objects/any_attribute.rb +30 -0
- data/lib/xsd/objects/appinfo.rb +13 -0
- data/lib/xsd/objects/attribute.rb +72 -0
- data/lib/xsd/objects/attribute_group.rb +18 -0
- data/lib/xsd/objects/choice.rb +33 -0
- data/lib/xsd/objects/complex_content.rb +25 -0
- data/lib/xsd/objects/complex_type.rb +82 -0
- data/lib/xsd/objects/documentation.rb +18 -0
- data/lib/xsd/objects/element.rb +130 -0
- data/lib/xsd/objects/extension.rb +14 -0
- data/lib/xsd/objects/facet.rb +12 -0
- data/lib/xsd/objects/field.rb +13 -0
- data/lib/xsd/objects/group.rb +32 -0
- data/lib/xsd/objects/import.rb +67 -0
- data/lib/xsd/objects/key.rb +25 -0
- data/lib/xsd/objects/keyref.rb +33 -0
- data/lib/xsd/objects/list.rb +18 -0
- data/lib/xsd/objects/restriction.rb +49 -0
- data/lib/xsd/objects/schema.rb +155 -0
- data/lib/xsd/objects/selector.rb +15 -0
- data/lib/xsd/objects/sequence.rb +33 -0
- data/lib/xsd/objects/simple_content.rb +19 -0
- data/lib/xsd/objects/simple_type.rb +35 -0
- data/lib/xsd/objects/union.rb +23 -0
- data/lib/xsd/objects/unique.rb +18 -0
- data/lib/xsd/shared/attribute_container.rb +17 -0
- data/lib/xsd/shared/based.rb +37 -0
- data/lib/xsd/shared/complex_typed.rb +28 -0
- data/lib/xsd/shared/element_container.rb +12 -0
- data/lib/xsd/shared/min_max_occurs.rb +46 -0
- data/lib/xsd/shared/referenced.rb +24 -0
- data/lib/xsd/shared/simple_typed.rb +14 -0
- data/lib/xsd/validator.rb +90 -0
- data/lib/xsd/version.rb +5 -0
- data/lib/xsd/xml.rb +111 -0
- data/lib/xsd.rb +42 -0
- data/xsd.gemspec +45 -0
- metadata +239 -0
@@ -0,0 +1,155 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module XSD
|
4
|
+
# The schema element defines the root element of a schema.
|
5
|
+
# Parent elements: NONE
|
6
|
+
# https://www.w3schools.com/xml/el_schema.asp
|
7
|
+
class Schema < BaseObject
|
8
|
+
include AttributeContainer
|
9
|
+
include ElementContainer
|
10
|
+
|
11
|
+
# Optional. The form for attributes declared in the target namespace of this schema. The value must be "qualified"
|
12
|
+
# or "unqualified". Default is "unqualified". "unqualified" indicates that attributes from the target namespace
|
13
|
+
# are not required to be qualified with the namespace prefix. "qualified" indicates that attributes from the target
|
14
|
+
# namespace must be qualified with the namespace prefix
|
15
|
+
# @!attribute attribute_form_default
|
16
|
+
# @return [String]
|
17
|
+
property :attributeFormDefault, :string, default: 'unqualified'
|
18
|
+
|
19
|
+
# Optional. The form for elements declared in the target namespace of this schema. The value must be "qualified"
|
20
|
+
# or "unqualified". Default is "unqualified". "unqualified" indicates that elements from the target namespace are
|
21
|
+
# not required to be qualified with the namespace prefix. "qualified" indicates that elements from the target
|
22
|
+
# namespace must be qualified with the namespace prefix
|
23
|
+
# @!attribute element_form_default
|
24
|
+
# @return [String]
|
25
|
+
property :elementFormDefault, :string, default: 'unqualified'
|
26
|
+
|
27
|
+
# Optional. Specifies the default value of the block attribute on element and complexType elements in the target
|
28
|
+
# namespace. The block attribute prevents a complex type (or element) that has a specified type of derivation from
|
29
|
+
# being used in place of this complex type. This value can contain #all or a list that is a subset of extension,
|
30
|
+
# restriction, or substitution:
|
31
|
+
# extension - prevents complex types derived by extension
|
32
|
+
# restriction - prevents complex types derived by restriction
|
33
|
+
# substitution - prevents substitution of elements
|
34
|
+
# #all - prevents all derived complex types
|
35
|
+
# @!attribute block_default
|
36
|
+
# @return [String]
|
37
|
+
property :blockDefault, :string
|
38
|
+
|
39
|
+
# Optional. Specifies the default value of the final attribute on element, simpleType, and complexType elements in
|
40
|
+
# the target namespace. The final attribute prevents a specified type of derivation of an element, simpleType, or
|
41
|
+
# complexType element. For element and complexType elements, this value can contain #all or a list that is a subset
|
42
|
+
# of extension or restriction. For simpleType elements, this value can additionally contain list and union:
|
43
|
+
# extension - prevents derivation by extension
|
44
|
+
# restriction - prevents derivation by restriction
|
45
|
+
# list - prevents derivation by list
|
46
|
+
# union - prevents derivation by union
|
47
|
+
# #all - prevents all derivation
|
48
|
+
# @!attribute final_default
|
49
|
+
# @return [String]
|
50
|
+
property :finalDefault, :string
|
51
|
+
|
52
|
+
# Optional. A URI reference of the namespace of this schema
|
53
|
+
# @!attribute target_namespace
|
54
|
+
# @return [String]
|
55
|
+
property :targetNamespace, :string
|
56
|
+
|
57
|
+
# Optional. Specifies the version of the schema
|
58
|
+
# @!attribute version
|
59
|
+
# @return [String]
|
60
|
+
property :version, :string
|
61
|
+
|
62
|
+
# A URI reference that specifies one or more namespaces for use in this schema. If no prefix is assigned, the schema
|
63
|
+
# components of the namespace can be used with unqualified references
|
64
|
+
# @!attribute xmlns
|
65
|
+
# @return [String]
|
66
|
+
property :xmlns, :string
|
67
|
+
|
68
|
+
# Global complex types
|
69
|
+
# @!attribute complex_types
|
70
|
+
# @return [Array<ComplexType>]
|
71
|
+
child :complex_types, [:complexType]
|
72
|
+
|
73
|
+
# Global simple types
|
74
|
+
# @!attribute simple_types
|
75
|
+
# @return [Array<SimpleType>]
|
76
|
+
child :simple_types, [:simpleType]
|
77
|
+
|
78
|
+
# Global groups
|
79
|
+
# @!attribute groups
|
80
|
+
# @return [Array<Group>]
|
81
|
+
child :groups, [:group]
|
82
|
+
|
83
|
+
# Get nested groups
|
84
|
+
# @!attribute imports
|
85
|
+
# @return [Array<Import>]
|
86
|
+
child :imports, [:import]
|
87
|
+
|
88
|
+
# Get current schema object
|
89
|
+
# @return [Schema]
|
90
|
+
def schema
|
91
|
+
self
|
92
|
+
end
|
93
|
+
|
94
|
+
# Get all available root elements. Overrides base implementation for better speed
|
95
|
+
# @return [Array<Element>]
|
96
|
+
def all_elements(*)
|
97
|
+
elements
|
98
|
+
end
|
99
|
+
|
100
|
+
# Get all available root attributes. Overrides base implementation for better speed
|
101
|
+
# @return [Array<Attribute>]
|
102
|
+
def all_attributes(*)
|
103
|
+
attributes
|
104
|
+
end
|
105
|
+
|
106
|
+
# Get target namespace prefix. There may be more than one prefix, but we return only first defined
|
107
|
+
# @return [String]
|
108
|
+
def target_namespace_prefix
|
109
|
+
@target_namespace_prefix ||= namespaces.key(target_namespace)&.sub(/^xmlns:?/, '') || ''
|
110
|
+
end
|
111
|
+
|
112
|
+
# Get schema namespace prefix
|
113
|
+
# @return [String]
|
114
|
+
def namespace_prefix
|
115
|
+
@namespace_prefix ||= namespaces.key(XML_SCHEMA).sub(/^xmlns:?/, '')
|
116
|
+
end
|
117
|
+
|
118
|
+
# Check if namespace is a target namespace
|
119
|
+
# @param [String] prefix
|
120
|
+
# @return [Boolean]
|
121
|
+
def targets_namespace?(prefix)
|
122
|
+
namespaces[prefix.empty? ? 'xmlns' : "xmlns:#{prefix}"] == target_namespace
|
123
|
+
end
|
124
|
+
|
125
|
+
# Override map_children on schema to get objects from all imported schemas
|
126
|
+
# @param [Symbol] name
|
127
|
+
# @return [Array<BaseObject>]
|
128
|
+
def map_children(name, cache = {})
|
129
|
+
super(name) + import_map_children(name, cache)
|
130
|
+
end
|
131
|
+
|
132
|
+
# Get children from all imported schemas
|
133
|
+
# @param [Symbol] name
|
134
|
+
# @return [Array<BaseObject>]
|
135
|
+
# TODO: better recursion handling, may be refactor needed 1 reader for all schemas with centralized cache
|
136
|
+
def import_map_children(name, cache)
|
137
|
+
return [] if name.to_sym == :import
|
138
|
+
|
139
|
+
imports.map do |import|
|
140
|
+
if cache[import.namespace]
|
141
|
+
reader.logger.debug(XSD) { "Schema '#{import.namespace}' already parsed, skiping" }
|
142
|
+
nil
|
143
|
+
else
|
144
|
+
cache[import.namespace] = true
|
145
|
+
import.imported_reader.schema.map_children(name, cache)
|
146
|
+
end
|
147
|
+
end.compact.flatten
|
148
|
+
end
|
149
|
+
|
150
|
+
def import_by_namespace(ns)
|
151
|
+
aliases = [ns, namespaces["xmlns:#{(ns || '').gsub(/^xmlns:/, '')}"]].compact
|
152
|
+
imports.find { |import| aliases.include?(import.namespace) }
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module XSD
|
4
|
+
# The selector element specifies an XPath expression that selects a set of elements for an identity constraint
|
5
|
+
# (unique, key, and keyref elements).
|
6
|
+
# Parent elements: key, keyref, unique
|
7
|
+
# https://www.w3schools.com/xml/el_selector.asp
|
8
|
+
class Selector < BaseObject
|
9
|
+
# Required. Specifies an XPath expression, relative to the element being declared, that identifies the child
|
10
|
+
# elements to which the identity constraint applies
|
11
|
+
# @!attribute xpath
|
12
|
+
# @return [String]
|
13
|
+
property :xpath, :string, required: true
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module XSD
|
4
|
+
# The sequence element specifies that the child elements must appear in a sequence.
|
5
|
+
# Each child element can occur from 0 to any number of times.
|
6
|
+
# Parent elements: group, choice, sequence, complexType, restriction (both simpleContent and complexContent),
|
7
|
+
# extension (both simpleContent and complexContent)
|
8
|
+
# https://www.w3schools.com/xml/el_sequence.asp
|
9
|
+
class Sequence < BaseObject
|
10
|
+
include MinMaxOccurs
|
11
|
+
include ElementContainer
|
12
|
+
|
13
|
+
# Nested groups
|
14
|
+
# @!attribute groups
|
15
|
+
# @return [Array<Group>]
|
16
|
+
child :groups, [:group]
|
17
|
+
|
18
|
+
# Nested choices
|
19
|
+
# @!attribute choices
|
20
|
+
# @return [Array<Choice>]
|
21
|
+
child :choices, [:choice]
|
22
|
+
|
23
|
+
# Nested sequences
|
24
|
+
# @!attribute sequences
|
25
|
+
# @return [Array<Sequence>]
|
26
|
+
child :sequences, [:sequence]
|
27
|
+
|
28
|
+
# Nested anys
|
29
|
+
# @!attribute anys
|
30
|
+
# @return [Array<Any>]
|
31
|
+
child :anys, [:any]
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module XSD
|
4
|
+
# The simpleContent element contains extensions or restrictions on a text-only complex type or on a simple type as
|
5
|
+
# content and contains no elements.
|
6
|
+
# Parent elements: complexType
|
7
|
+
# https://www.w3schools.com/xml/el_simpleContent.asp
|
8
|
+
class SimpleContent < BaseObject
|
9
|
+
# Nested extension
|
10
|
+
# @!attribute extension
|
11
|
+
# @return [Extension, nil]
|
12
|
+
child :extension, :extension
|
13
|
+
|
14
|
+
# Nested restriction
|
15
|
+
# @!attribute restriction
|
16
|
+
# @return [Restriction, nil]
|
17
|
+
child :restriction, :restriction
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module XSD
|
4
|
+
# The simpleType element defines a simple type and specifies the constraints and information about the values
|
5
|
+
# of attributes or text-only elements.
|
6
|
+
# Parent elements: attribute, element, list, restriction, schema, union
|
7
|
+
# https://www.w3schools.com/xml/el_simpletype.asp
|
8
|
+
class SimpleType < BaseObject
|
9
|
+
# Optional. Specifies the name of the attribute. Name and ref attributes cannot both be present
|
10
|
+
# @!attribute name
|
11
|
+
# @return [String]
|
12
|
+
property :name, :string
|
13
|
+
|
14
|
+
# Nested restriction
|
15
|
+
# @!attribute restriction
|
16
|
+
# @return [Restriction, nil]
|
17
|
+
child :restriction, :restriction
|
18
|
+
|
19
|
+
# Nested union
|
20
|
+
# @!attribute union
|
21
|
+
# @return [Union, nil]
|
22
|
+
child :union, :union
|
23
|
+
|
24
|
+
# Nested list
|
25
|
+
# @!attribute list
|
26
|
+
# @return [List, nil]
|
27
|
+
child :list, :list
|
28
|
+
|
29
|
+
# Determine if this is a linked type
|
30
|
+
# @return [Boolean]
|
31
|
+
def linked?
|
32
|
+
!name.nil?
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module XSD
|
4
|
+
# The union element defines a simple type as a collection (union) of values from specified simple data types.
|
5
|
+
# Parent elements: simpleType
|
6
|
+
# https://www.w3schools.com/xml/el_union.asp
|
7
|
+
class Union < BaseObject
|
8
|
+
# Optional. Specifies a list of built-in data types or simpleType elements defined in a schema
|
9
|
+
# @!attribute member_types
|
10
|
+
# @return [Array<String>]
|
11
|
+
property :memberTypes, :array, default: [] do |union|
|
12
|
+
union.node['memberTypes']&.split(' ')
|
13
|
+
end
|
14
|
+
|
15
|
+
# Nested simple and built-in types
|
16
|
+
# @return [Array<SimpleType, String>]
|
17
|
+
def types
|
18
|
+
@types ||= map_children(:simpleType) + member_types.map do |name|
|
19
|
+
object_by_name(:simpleType, name) || name
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module XSD
|
4
|
+
# The unique element defines that an element or an attribute value must be unique within the scope.
|
5
|
+
# Parent elements: element
|
6
|
+
# https://www.w3schools.com/xml/el_unique.asp
|
7
|
+
class Unique < BaseObject
|
8
|
+
# Get nested selector object
|
9
|
+
# @!attribute selector
|
10
|
+
# @return [Selector]
|
11
|
+
child :selector, :selector
|
12
|
+
|
13
|
+
# Get nested field objects
|
14
|
+
# @!attribute fields
|
15
|
+
# @return [Array<Field>]
|
16
|
+
child :fields, [:field]
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module XSD
|
4
|
+
module AttributeContainer
|
5
|
+
# Nested attributes
|
6
|
+
# @!attribute attributes
|
7
|
+
# @return [Array<Attribute>]
|
8
|
+
|
9
|
+
# Nested attribute groups
|
10
|
+
# @!attribute attribute_groups
|
11
|
+
# @return [Array<AttributeGroup>]
|
12
|
+
def self.included(obj)
|
13
|
+
obj.child :attributes, [:attribute]
|
14
|
+
obj.child :attribute_groups, [:attributeGroup]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module XSD
|
4
|
+
# Used by extension and restriction elements
|
5
|
+
module Based
|
6
|
+
# Required. Specifies the name of a built-in data type, a simpleType element, or a complexType element
|
7
|
+
# @!attribute base
|
8
|
+
# @return [String]
|
9
|
+
|
10
|
+
# Base complexType
|
11
|
+
# @!attribute base_complex_type
|
12
|
+
# @return [ComplexType, nil]
|
13
|
+
|
14
|
+
# Base simpleType
|
15
|
+
# @!attribute base_simple_type
|
16
|
+
# @return [SimpleType, nil]
|
17
|
+
def self.included(obj)
|
18
|
+
obj.property :base, :string, required: true
|
19
|
+
obj.link :base_complex_type, :complexType, property: :base
|
20
|
+
obj.link :base_simple_type, :simpleType, property: :base
|
21
|
+
end
|
22
|
+
|
23
|
+
# Get all available elements on the current stack level, optionally including base type elements
|
24
|
+
# @param [Boolean] include_base
|
25
|
+
# @return [Array<Element>]
|
26
|
+
def all_elements(include_base = true)
|
27
|
+
(include_base && base_complex_type ? base_complex_type.all_elements : []) + super
|
28
|
+
end
|
29
|
+
|
30
|
+
# Get all available attributes on the current stack level, optionally including base type attributes
|
31
|
+
# @param [Boolean] include_base
|
32
|
+
# @return [Array<Attribute>]
|
33
|
+
def all_attributes(include_base = true)
|
34
|
+
(include_base && base_complex_type ? base_complex_type.all_attributes : []) + super
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module XSD
|
4
|
+
# Provides object an ability to have complex type (nested or linked)
|
5
|
+
module ComplexTyped
|
6
|
+
# Child/linked complex type
|
7
|
+
# @!attribute complex_type
|
8
|
+
# @return [ComplexType, nil]
|
9
|
+
def self.included(obj)
|
10
|
+
obj.child :complex_type, :complexType
|
11
|
+
obj.link :complex_type, :complexType, property: obj::TYPE_PROPERTY
|
12
|
+
end
|
13
|
+
|
14
|
+
# Get all available elements on the current stack level or linked type elements
|
15
|
+
# @param [Boolean] linked_type
|
16
|
+
# @return [Array<Element>]
|
17
|
+
def all_elements(linked_type = true)
|
18
|
+
(linked_type && complex_type&.linked? ? complex_type.all_elements : super)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Get all available attributes on the current stack level or linked type attributes
|
22
|
+
# @param [Boolean] linked_type
|
23
|
+
# @return [Array<Attribute>]
|
24
|
+
def all_attributes(linked_type = true)
|
25
|
+
(linked_type && complex_type&.linked? ? complex_type.all_attributes : super)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module XSD
|
4
|
+
module MinMaxOccurs
|
5
|
+
# Optional. Specifies the minimum number of times the choice element can occur in the parent the element.
|
6
|
+
# The value can be any number >= 0. Default value is 1
|
7
|
+
# @!attribute min_occurs
|
8
|
+
# @return [Integer]
|
9
|
+
|
10
|
+
# Optional. Specifies the maximum number of times the choice element can occur in the parent element.
|
11
|
+
# The value can be any number >= 0, or if you want to set no limit on the maximum number, use the value "unbounded".
|
12
|
+
# Default value is 1
|
13
|
+
# @!attribute max_occurs
|
14
|
+
# @return [Integer, Symbol]
|
15
|
+
def self.included(obj)
|
16
|
+
obj.property :minOccurs, :integer, default: 1
|
17
|
+
obj.property :maxOccurs, :integer, default: 1
|
18
|
+
end
|
19
|
+
|
20
|
+
# Compute actual max_occurs accounting parents
|
21
|
+
# @return [Integer, Symbol]
|
22
|
+
def computed_max_occurs
|
23
|
+
@computed_max_occurs ||= if parent.is_a?(MinMaxOccurs)
|
24
|
+
if max_occurs == :unbounded || parent.computed_max_occurs == :unbounded
|
25
|
+
:unbounded
|
26
|
+
else
|
27
|
+
[max_occurs, parent.computed_max_occurs].max
|
28
|
+
end
|
29
|
+
else
|
30
|
+
max_occurs
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Compute actual min_occurs accounting parents
|
35
|
+
# @return [Integer]
|
36
|
+
def computed_min_occurs
|
37
|
+
@computed_min_occurs ||= if parent.is_a?(Choice)
|
38
|
+
0
|
39
|
+
elsif parent.is_a?(MinMaxOccurs)
|
40
|
+
[min_occurs, parent.computed_min_occurs].min
|
41
|
+
else
|
42
|
+
min_occurs
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module XSD
|
4
|
+
module Referenced
|
5
|
+
# Optional. Specifies a reference to a named attribute. Name and ref attributes cannot both be present.
|
6
|
+
# If ref is present, simpleType element, form, and type cannot be present
|
7
|
+
# @!attribute ref
|
8
|
+
# @return [String]
|
9
|
+
|
10
|
+
# Reference object
|
11
|
+
# @!attribute reference
|
12
|
+
# @return [BaseObject]
|
13
|
+
def self.included(obj)
|
14
|
+
obj.property :ref, :string
|
15
|
+
obj.link :reference, obj.mapped_name, property: :ref
|
16
|
+
end
|
17
|
+
|
18
|
+
# Is object referenced?
|
19
|
+
# @return [Boolean]
|
20
|
+
def referenced?
|
21
|
+
!ref.nil?
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module XSD
|
4
|
+
# Provides object an ability to have simple type (nested or linked)
|
5
|
+
module SimpleTyped
|
6
|
+
# Get child/linked simple type
|
7
|
+
# @!attribute simple_type
|
8
|
+
# @return [SimpleType, nil]
|
9
|
+
def self.included(obj)
|
10
|
+
obj.child :simple_type, :simpleType
|
11
|
+
obj.link :simple_type, :simpleType, property: obj::TYPE_PROPERTY
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module XSD
|
4
|
+
module Validator
|
5
|
+
# Validate XML against XSD
|
6
|
+
# @param [String, Pathname, Nokogiri::XML::Document] xml
|
7
|
+
def validate_xml(xml)
|
8
|
+
# validate input
|
9
|
+
raise ValidationError unless xml.is_a?(Nokogiri::XML::Document) || xml.is_a?(Pathname) || xml.is_a?(String)
|
10
|
+
|
11
|
+
begin
|
12
|
+
document = xml.is_a?(Nokogiri::XML::Document) ? xml : Nokogiri::XML(xml)
|
13
|
+
rescue Nokogiri::XML::SyntaxError => e
|
14
|
+
raise ValidationError, e
|
15
|
+
end
|
16
|
+
|
17
|
+
errors = schema_validator.validate(document)
|
18
|
+
raise ValidationError, errors.map(&:message).join('; ') if errors.any?
|
19
|
+
end
|
20
|
+
|
21
|
+
# Validate XSD against another XSD (by default uses XMLSchema 1.0)
|
22
|
+
def validate
|
23
|
+
begin
|
24
|
+
schema_validator
|
25
|
+
rescue Nokogiri::XML::SyntaxError => e
|
26
|
+
# TODO: display import map name for imported_xsd
|
27
|
+
message = e.message + (e.file ? " in file '#{File.basename(e.file)}'" : '')
|
28
|
+
raise ValidationError, message
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
# Get Nokogiri::XML::Schema object to validate against
|
35
|
+
# @return [Nokogiri::XML::Schema]
|
36
|
+
def schema_validator
|
37
|
+
return @schema_validator if @schema_validator
|
38
|
+
|
39
|
+
if !imported_xsd.empty?
|
40
|
+
# imports are explicitly provided - put all files in one tmpdir and update import paths appropriately
|
41
|
+
# TODO: save file/path map to display in errors
|
42
|
+
Dir.mktmpdir('XSD', tmp_dir) do |dir|
|
43
|
+
# create primary xsd file
|
44
|
+
file = "#{SecureRandom.urlsafe_base64}.xsd"
|
45
|
+
|
46
|
+
# create imported xsd files
|
47
|
+
recursive_import_xsd(self, file, Set.new) do |f, data|
|
48
|
+
File.write("#{dir}/#{f}", data)
|
49
|
+
end
|
50
|
+
|
51
|
+
# read schema from tmp file descriptor
|
52
|
+
io = File.open("#{dir}/#{file}")
|
53
|
+
@schema_validator = create_xml_schema(io)
|
54
|
+
end
|
55
|
+
else
|
56
|
+
@schema_validator = create_xml_schema(self.xsd)
|
57
|
+
end
|
58
|
+
|
59
|
+
@schema_validator
|
60
|
+
end
|
61
|
+
|
62
|
+
# Сформировать имена файлов и содержимое XSD схем для корректной валидации
|
63
|
+
# @param [XML] reader
|
64
|
+
# @param [String] file
|
65
|
+
# @param [Set] processed
|
66
|
+
def recursive_import_xsd(reader, file, processed, &block)
|
67
|
+
# handle recursion
|
68
|
+
namespace = reader.schema.target_namespace
|
69
|
+
return if processed.include?(namespace)
|
70
|
+
|
71
|
+
processed.add(namespace)
|
72
|
+
|
73
|
+
data = reader.xml
|
74
|
+
|
75
|
+
reader.imports.each do |import|
|
76
|
+
name = "#{SecureRandom.urlsafe_base64}.xsd"
|
77
|
+
data = data.sub("schemaLocation=\"#{import.schema_location}\"", "schemaLocation=\"#{name}\"")
|
78
|
+
recursive_import_xsd(import.imported_reader, name, processed, &block)
|
79
|
+
end
|
80
|
+
|
81
|
+
block.call(file, data)
|
82
|
+
end
|
83
|
+
|
84
|
+
# Create Nokogiri XML Schema instance with preconfigured options
|
85
|
+
# @param [IO] io
|
86
|
+
def create_xml_schema(io)
|
87
|
+
Nokogiri::XML::Schema(io, Nokogiri::XML::ParseOptions.new.nononet)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|