tilia-xml 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +19 -0
  3. data/.rubocop.yml +32 -0
  4. data/.simplecov +4 -0
  5. data/.travis.yml +3 -0
  6. data/CHANGELOG.sabre.md +167 -0
  7. data/CONTRIBUTING.md +25 -0
  8. data/Gemfile +15 -0
  9. data/Gemfile.lock +56 -0
  10. data/LICENSE +27 -0
  11. data/LICENSE.sabre +27 -0
  12. data/README.md +30 -0
  13. data/Rakefile +17 -0
  14. data/lib/tilia/xml/context_stack_trait.rb +99 -0
  15. data/lib/tilia/xml/element/base.rb +73 -0
  16. data/lib/tilia/xml/element/cdata.rb +53 -0
  17. data/lib/tilia/xml/element/elements.rb +109 -0
  18. data/lib/tilia/xml/element/key_value.rb +110 -0
  19. data/lib/tilia/xml/element/uri.rb +98 -0
  20. data/lib/tilia/xml/element/xml_fragment.rb +128 -0
  21. data/lib/tilia/xml/element.rb +22 -0
  22. data/lib/tilia/xml/lib_xml_exception.rb +9 -0
  23. data/lib/tilia/xml/parse_exception.rb +7 -0
  24. data/lib/tilia/xml/reader.rb +240 -0
  25. data/lib/tilia/xml/service.rb +151 -0
  26. data/lib/tilia/xml/version.rb +9 -0
  27. data/lib/tilia/xml/writer.rb +261 -0
  28. data/lib/tilia/xml/xml_deserializable.rb +29 -0
  29. data/lib/tilia/xml/xml_serializable.rb +27 -0
  30. data/lib/tilia/xml.rb +23 -0
  31. data/test/test_helper.rb +4 -0
  32. data/test/xml/context_stack_test.rb +40 -0
  33. data/test/xml/element/cdata_test.rb +37 -0
  34. data/test/xml/element/eater.rb +60 -0
  35. data/test/xml/element/elements_test.rb +113 -0
  36. data/test/xml/element/key_value_test.rb +187 -0
  37. data/test/xml/element/mock.rb +52 -0
  38. data/test/xml/element/uri_test.rb +55 -0
  39. data/test/xml/element/xml_fragment_test.rb +121 -0
  40. data/test/xml/infite_loop_test.rb +47 -0
  41. data/test/xml/reader_test.rb +407 -0
  42. data/test/xml/service_test.rb +156 -0
  43. data/test/xml/writer_test.rb +260 -0
  44. data/tilia-xml.gemspec +15 -0
  45. metadata +132 -0
@@ -0,0 +1,240 @@
1
+ require 'libxml'
2
+ require 'stringio'
3
+ LibXML::XML::Error.set_handler(&LibXML::XML::Error::QUIET_HANDLER)
4
+
5
+ module Tilia
6
+ module Xml
7
+ # The Reader class expands upon PHP's built-in XMLReader.
8
+ #
9
+ # The intended usage, is to assign certain XML elements to PHP classes. These
10
+ # need to be registered using the element_map public property.
11
+ #
12
+ # After this is done, a single call to parse() will parse the entire document,
13
+ # and delegate sub-sections of the document to element classes.
14
+ class Reader
15
+ include Tilia::Xml::ContextStackTrait
16
+
17
+ # Returns the current nodename in clark-notation.
18
+ #
19
+ # For example: "{http://www.w3.org/2005/Atom}feed".
20
+ # Or if no namespace is defined: "{}feed".
21
+ #
22
+ # This method returns null if we're not currently on an element.
23
+ #
24
+ # @return [String, nil]
25
+ def clark
26
+ return nil unless local_name
27
+
28
+ "{#{namespace_uri}}#{local_name}"
29
+ end
30
+
31
+ # Reads the entire document.
32
+ #
33
+ # This function returns an array with the following three elements:
34
+ # * name - The root element name.
35
+ # * value - The value for the root element.
36
+ # * attributes - An array of attributes.
37
+ #
38
+ # This function will also disable the standard libxml error handler (which
39
+ # usually just results in PHP errors), and throw exceptions instead.
40
+ #
41
+ # @return [Hash]
42
+ def parse
43
+ begin
44
+ nil while node_type != ::LibXML::XML::Reader::TYPE_ELEMENT && read # noop
45
+
46
+ result = parse_current_element
47
+ rescue ::LibXML::XML::Error => e
48
+ raise Tilia::Xml::LibXmlException, e.to_s
49
+ end
50
+
51
+ result
52
+ end
53
+
54
+ # parse_get_elements parses everything in the current sub-tree,
55
+ # and returns a an array of elements.
56
+ #
57
+ # Each element has a 'name', 'value' and 'attributes' key.
58
+ #
59
+ # If the the element didn't contain sub-elements, an empty array is always
60
+ # returned. If there was any text inside the element, it will be
61
+ # discarded.
62
+ #
63
+ # If the element_map argument is specified, the existing element_map will
64
+ # be overridden while parsing the tree, and restored after this process.
65
+ #
66
+ # @param [Hash] element_map
67
+ # @return [Array]
68
+ def parse_get_elements(element_map = nil)
69
+ result = parse_inner_tree(element_map)
70
+
71
+ return [] unless result.is_a?(Array)
72
+ result
73
+ end
74
+
75
+ # Parses all elements below the current element.
76
+ #
77
+ # This method will return a string if this was a text-node, or an array if
78
+ # there were sub-elements.
79
+ #
80
+ # If there's both text and sub-elements, the text will be discarded.
81
+ #
82
+ # If the element_map argument is specified, the existing element_map will
83
+ # be overridden while parsing the tree, and restored after this process.
84
+ #
85
+ # @param [Hash] element_map
86
+ # @return [Array, String]
87
+ def parse_inner_tree(element_map = nil)
88
+ text = nil
89
+ elements = []
90
+
91
+ if node_type == ::LibXML::XML::Reader::TYPE_ELEMENT && self.empty_element?
92
+ # Easy!
93
+ self.next
94
+ return nil
95
+ end
96
+
97
+ unless element_map.nil?
98
+ push_context
99
+ @element_map = element_map
100
+ end
101
+
102
+ return false unless read
103
+
104
+ loop do
105
+ # RUBY: Skip is_valid block
106
+
107
+ case node_type
108
+ when ::LibXML::XML::Reader::TYPE_ELEMENT
109
+ elements << parse_current_element
110
+ when ::LibXML::XML::Reader::TYPE_TEXT,
111
+ ::LibXML::XML::Reader::TYPE_CDATA
112
+ text ||= ''
113
+ text += value
114
+ read
115
+ when ::LibXML::XML::Reader::TYPE_END_ELEMENT
116
+ # Ensuring we are moving the cursor after the end element.
117
+ read
118
+ break
119
+ when ::LibXML::XML::Reader::TYPE_NONE
120
+ fail Tilia::Xml::ParseException, 'We hit the end of the document prematurely. This likely means that some parser "eats" too many elements. Do not attempt to continue parsing.'
121
+ else
122
+ # Advance to the next element
123
+ read
124
+ end
125
+ end
126
+
127
+ pop_context unless element_map.nil?
128
+
129
+ elements.any? ? elements : text
130
+ end
131
+
132
+ # Reads all text below the current element, and returns this as a string.
133
+ #
134
+ # @return [String]
135
+ def read_text
136
+ result = ''
137
+ previous_depth = depth
138
+
139
+ while read && depth != previous_depth
140
+ result += value if [
141
+ ::LibXML::XML::Reader::TYPE_TEXT,
142
+ ::LibXML::XML::Reader::TYPE_CDATA,
143
+ ::LibXML::XML::Reader::TYPE_WHITESPACE
144
+ ].include? node_type
145
+ end
146
+
147
+ result
148
+ end
149
+
150
+ # Parses the current XML element.
151
+ #
152
+ # This method returns arn array with 3 properties:
153
+ # * name - A clark-notation XML element name.
154
+ # * value - The parsed value.
155
+ # * attributes - A key-value list of attributes.
156
+ #
157
+ # @return [Hash]
158
+ def parse_current_element
159
+ name = clark
160
+
161
+ attributes = {}
162
+
163
+ attributes = parse_attributes if self.has_attributes?
164
+
165
+ if @element_map.key? name
166
+ deserializer = @element_map[name]
167
+
168
+ if deserializer.is_a?(Class) && deserializer.include?(XmlDeserializable)
169
+ value = deserializer.xml_deserialize(self)
170
+ elsif deserializer.is_a? Proc
171
+ value = deserializer.call(self)
172
+ else
173
+ # Omit php stuff for error creation
174
+ fail "Could not use this type as a deserializer: #{deserializer.inspect}"
175
+ end
176
+ else
177
+ value = Element::Base.xml_deserialize(self)
178
+ end
179
+ {
180
+ 'name' => name,
181
+ 'value' => value,
182
+ 'attributes' => attributes
183
+ }
184
+ end
185
+
186
+ # Grabs all the attributes from the current element, and returns them as a
187
+ # key-value array.
188
+ #
189
+ # If the attributes are part of the same namespace, they will simply be
190
+ # short keys. If they are defined on a different namespace, the attribute
191
+ # name will be retured in clark-notation.
192
+ #
193
+ # @return [Hash]
194
+ def parse_attributes
195
+ attributes = {}
196
+
197
+ while move_to_next_attribute != 0
198
+ if namespace_uri
199
+ # Ignoring 'xmlns', it doesn't make any sense.
200
+ next if namespace_uri == 'http://www.w3.org/2000/xmlns/'
201
+
202
+ name = clark
203
+ attributes[name] = value
204
+ else
205
+ attributes[local_name] = value
206
+ end
207
+ end
208
+
209
+ move_to_element
210
+
211
+ attributes
212
+ end
213
+
214
+ # TODO: document this
215
+ def initialize
216
+ initialize_context_stack_attributes
217
+ end
218
+
219
+ # TODO: documentation
220
+ def xml(input)
221
+ fail 'XML document already loaded' if @reader
222
+
223
+ if input.is_a? String
224
+ @reader = ::LibXML::XML::Reader.string(input)
225
+ elsif input.is_a? File
226
+ @reader = ::LibXML::XML::Reader.file(input)
227
+ elsif input.is_a? StringIO
228
+ @reader = ::LibXML::XML::Reader.io(input)
229
+ else
230
+ fail 'Unable to load XML document'
231
+ end
232
+ end
233
+
234
+ # TODO: documentation
235
+ def method_missing(name, *args)
236
+ @reader.send(name, *args)
237
+ end
238
+ end
239
+ end
240
+ end
@@ -0,0 +1,151 @@
1
+ module Tilia
2
+ module Xml
3
+ # XML parsing and writing service.
4
+ #
5
+ # You are encouraged to make a instance of this for your application and
6
+ # potentially extend it, as a central API point for dealing with xml and
7
+ # configuring the reader and writer.
8
+ class Service
9
+ # This is the element map. It contains a list of XML elements (in clark
10
+ # notation) as keys and PHP class names as values.
11
+ #
12
+ # The PHP class names must implement Sabre\Xml\Element.
13
+ #
14
+ # Values may also be a callable. In that case the function will be called
15
+ # directly.
16
+ #
17
+ # @return [Hash]
18
+ attr_accessor :element_map
19
+
20
+ # This is a list of namespaces that you want to give default prefixes.
21
+ #
22
+ # You must make sure you create this entire list before starting to write.
23
+ # They should be registered on the root element.
24
+ #
25
+ # @return [Hash]
26
+ attr_accessor :namespace_map
27
+
28
+ # Returns a fresh XML Reader
29
+ #
30
+ # @return [Reader]
31
+ def reader
32
+ reader = Reader.new
33
+ reader.element_map = @element_map
34
+ reader
35
+ end
36
+
37
+ # Returns a fresh xml writer
38
+ #
39
+ # @return [Writer]
40
+ def writer
41
+ writer = Writer.new
42
+ writer.namespace_map = @namespace_map
43
+ writer
44
+ end
45
+
46
+ # Parses a document in full.
47
+ #
48
+ # Input may be specified as a string or readable stream resource.
49
+ # The returned value is the value of the root document.
50
+ #
51
+ # Specifying the context_uri allows the parser to figure out what the URI
52
+ # of the document was. This allows relative URIs within the document to be
53
+ # expanded easily.
54
+ #
55
+ # The root_element_name is specified by reference and will be populated
56
+ # with the root element name of the document.
57
+ #
58
+ # @param [String, File, StringIO] input
59
+ # @param [String, nil] context_uri
60
+ # @param [String, nil] root_element_name
61
+ # @raise [ParseException]
62
+ # @return [Array, Object, String]
63
+ def parse(input, context_uri = nil, root_element_name = nil)
64
+ # Skip php short commings
65
+ reader = self.reader
66
+ reader.context_uri = context_uri
67
+ reader.xml(input)
68
+
69
+ result = reader.parse
70
+ root_element_name.replace(result['name'])
71
+ result['value']
72
+ end
73
+
74
+ # Parses a document in full, and specify what the expected root element
75
+ # name is.
76
+ #
77
+ # This function works similar to parse, but the difference is that the
78
+ # user can specify what the expected name of the root element should be,
79
+ # in clark notation.
80
+ #
81
+ # This is useful in cases where you expected a specific document to be
82
+ # passed, and reduces the amount of if statements.
83
+ #
84
+ # @param [String] root_element_name
85
+ # @param [String, File, StringIO] input
86
+ # @param [String, nil] context_uri
87
+ # @return [void]
88
+ def expect(root_element_name, input, context_uri = nil)
89
+ # Skip php short commings
90
+ reader = self.reader
91
+ reader.context_uri = context_uri
92
+ reader.xml(input)
93
+
94
+ result = reader.parse
95
+ if root_element_name != result['name']
96
+ fail Tilia::Xml::ParseException, "Expected #{root_element_name} but received #{result['name']} as the root element"
97
+ end
98
+ result['value']
99
+ end
100
+
101
+ # Generates an XML document in one go.
102
+ #
103
+ # The $rootElement must be specified in clark notation.
104
+ # The value must be a string, an array or an object implementing
105
+ # XmlSerializable. Basically, anything that's supported by the Writer
106
+ # object.
107
+ #
108
+ # context_uri can be used to specify a sort of 'root' of the PHP application,
109
+ # in case the xml document is used as a http response.
110
+ #
111
+ # This allows an implementor to easily create URI's relative to the root
112
+ # of the domain.
113
+ #
114
+ # @param [String] root_element_name
115
+ # @param [String, Array, XmlSerializable] value
116
+ # @param [String, nil] context_uri
117
+ # @return [void]
118
+ def write(root_element_name, value, context_uri = nil)
119
+ writer = self.writer
120
+ writer.open_memory
121
+ writer.context_uri = context_uri
122
+ writer.set_indent(true)
123
+ writer.start_document
124
+ writer.write_element(root_element_name, value)
125
+ writer.output_memory
126
+ end
127
+
128
+ # Parses a clark-notation string, and returns the namespace and element
129
+ # name components.
130
+ #
131
+ # If the string was invalid, it will throw an InvalidArgumentException.
132
+ #
133
+ # @param [String] str
134
+ # @raise [InvalidArgumentException]
135
+ # @return [Array]
136
+ def self.parse_clark_notation(str)
137
+ if str =~ /^{([^}]*)}(.*)/
138
+ [Regexp.last_match[1], Regexp.last_match[2]]
139
+ else
140
+ fail ArgumentError, "'#{str}' is not a valid clark-notation formatted string"
141
+ end
142
+ end
143
+
144
+ # TODO: document
145
+ def initialize
146
+ @element_map = {}
147
+ @namespace_map = {}
148
+ end
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,9 @@
1
+ module Tilia
2
+ module Xml
3
+ # This class contains the version number for this package.
4
+ class Version
5
+ # Full version number
6
+ VERSION = '1.2.0'
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,261 @@
1
+ require 'libxml'
2
+ module Tilia
3
+ module Xml
4
+ # The XML Writer class.
5
+ #
6
+ # This class works exactly as PHP's built-in XMLWriter, with a few additions.
7
+ #
8
+ # Namespaces can be registered beforehand, globally. When the first element is
9
+ # written, namespaces will automatically be declared.
10
+ #
11
+ # The write_attribute, startElement and write_element can now take a
12
+ # clark-notation element name (example: {http://www.w3.org/2005/Atom}link).
13
+ #
14
+ # If, when writing the namespace is a known one a prefix will automatically be
15
+ # selected, otherwise a random prefix will be generated.
16
+ #
17
+ # Instead of standard string values, the writer can take Element classes (as
18
+ # defined by this library) to delegate the serialization.
19
+ #
20
+ # The write() method can take array structures to quickly write out simple xml
21
+ # trees.
22
+ class Writer
23
+ include ContextStackTrait
24
+
25
+ protected
26
+
27
+ # Any namespace that the writer is asked to write, will be added here.
28
+ #
29
+ # Any of these elements will get a new namespace definition *every single
30
+ # time* they are used, but this array allows the writer to make sure that
31
+ # the prefixes are consistent anyway.
32
+ #
33
+ # @return [Hash]
34
+ attr_accessor :adhoc_namespaces
35
+
36
+ # When the first element is written, this flag is set to true.
37
+ #
38
+ # This ensures that the namespaces in the namespaces map are only written
39
+ # once.
40
+ #
41
+ # @return [Boolean]
42
+ attr_accessor :namespaces_written
43
+
44
+ public
45
+
46
+ # Writes a value to the output stream.
47
+ #
48
+ # The following values are supported:
49
+ # 1. Scalar values will be written as-is, as text.
50
+ # 2. Null values will be skipped (resulting in a short xml tag).
51
+ # 3. If a value is an instance of an Element class, writing will be
52
+ # delegated to the object.
53
+ # 4. If a value is an array, two formats are supported.
54
+ #
55
+ # Array format 1:
56
+ # [
57
+ # "{namespace}name1" => "..",
58
+ # "{namespace}name2" => "..",
59
+ # ]
60
+ #
61
+ # One element will be created for each key in this array. The values of
62
+ # this array support any format this method supports (this method is
63
+ # called recursively).
64
+ #
65
+ # Array format 2:
66
+ #
67
+ # [
68
+ # [
69
+ # "name" => "{namespace}name1"
70
+ # "value" => "..",
71
+ # "attributes" => [
72
+ # "attr" => "attribute value",
73
+ # ]
74
+ # ],
75
+ # [
76
+ # "name" => "{namespace}name1"
77
+ # "value" => "..",
78
+ # "attributes" => [
79
+ # "attr" => "attribute value",
80
+ # ]
81
+ # ]
82
+ # ]
83
+ #
84
+ # @param value
85
+ # @return [void]
86
+ def write(value)
87
+ if value.is_a?(Numeric) || value.is_a?(String)
88
+ write_string(value.to_s)
89
+ elsif value.is_a?(TrueClass) || value.is_a?(FalseClass)
90
+ write_string(value.to_s)
91
+ elsif value.is_a? XmlSerializable
92
+ value.xml_serialize(self)
93
+ elsif value.nil?
94
+ # noop
95
+ elsif value.is_a?(Hash) || value.is_a?(Array)
96
+ # Code for ruby implementation
97
+ if value.is_a?(Array)
98
+ hash = {}
99
+ value.each_with_index do |v, i|
100
+ hash[i] = v
101
+ end
102
+ value = hash
103
+ end
104
+
105
+ value.each do |name, item|
106
+ if name.is_a? Fixnum
107
+ # This item has a numeric index. We expect to be an array with a name and a value.
108
+ unless item.is_a?(Hash) && item.key?('name') && item.key?('value')
109
+ fail ArgumentError, 'When passing an array to ->write with numeric indices, every item must be an array containing the "name" and "value" key'
110
+ end
111
+
112
+ attributes = item.key?('attributes') ? item['attributes'] : []
113
+ name = item['name']
114
+ item = item['value']
115
+ elsif item.is_a?(Hash) && item.key?('value')
116
+ # This item has a text index. We expect to be an array with a value and optional attributes.
117
+ attributes = item.key?('attributes') ? item['attributes'] : []
118
+ item = item['value']
119
+ else
120
+ # If it's an array with text-indices, we expect every item's
121
+ # key to be an xml element name in clark notation.
122
+ # No attributes can be passed.
123
+ attributes = []
124
+ end
125
+
126
+ start_element(name)
127
+ write_attributes(attributes)
128
+ write(item)
129
+ end_element
130
+ end
131
+ else
132
+ fail ArgumentError, "The writer cannot serialize objects of type: #{value.class}"
133
+ end
134
+ end
135
+
136
+ # Starts an element.
137
+ #
138
+ # @param [String] name
139
+ # @return [Boolean]
140
+ def start_element(name)
141
+ if name[0] == '{'
142
+ (namespace, local_name) = Service.parse_clark_notation(name)
143
+
144
+ if @namespace_map.key? namespace
145
+ result = start_element_ns(@namespace_map[namespace], local_name, nil)
146
+ else
147
+ # An empty namespace means it's the global namespace. This is
148
+ # allowed, but it mustn't get a prefix.
149
+ if namespace == ''
150
+ result = start_element(local_name)
151
+ write_attribute('xmlns', '')
152
+ else
153
+ unless @adhoc_namespaces.key? namespace
154
+ @adhoc_namespaces[namespace] = 'x' + (@adhoc_namespaces.size + 1).to_s
155
+ end
156
+ result = start_element_ns(@adhoc_namespaces[namespace], local_name, namespace)
157
+ end
158
+ end
159
+ else
160
+ result = @writer.start_element(name)
161
+ end
162
+
163
+ unless @namespaces_written
164
+ @namespace_map.each do |ns, prefix|
165
+ write_attribute((prefix ? 'xmlns:' + prefix : 'xmlns'), ns)
166
+ end
167
+ @namespaces_written = true
168
+ end
169
+
170
+ result
171
+ end
172
+
173
+ # Write a full element tag.
174
+ #
175
+ # This method automatically closes the element as well.
176
+ #
177
+ # @param [String] name
178
+ # @param [String] content
179
+ # @return [Boolean]
180
+ def write_element(name, content = nil)
181
+ start_element(name)
182
+ write(content) unless content.nil?
183
+ end_element
184
+ end
185
+
186
+ # Writes a list of attributes.
187
+ #
188
+ # Attributes are specified as a key->value array.
189
+ #
190
+ # The key is an attribute name. If the key is a 'localName', the current
191
+ # xml namespace is assumed. If it's a 'clark notation key', this namespace
192
+ # will be used instead.
193
+ #
194
+ # @param [Hash] attributes
195
+ # @return [void]
196
+ def write_attributes(attributes)
197
+ attributes.each do |name, value|
198
+ write_attribute(name, value)
199
+ end
200
+ end
201
+
202
+ # Writes a new attribute.
203
+ #
204
+ # The name may be specified in clark-notation.
205
+ #
206
+ # Returns true when successful.
207
+ #
208
+ # @param [String] name
209
+ # @param [String] value
210
+ # @return [Boolean]
211
+ def write_attribute(name, value)
212
+ if name[0] == '{'
213
+ (namespace, local_name) = Service.parse_clark_notation(name)
214
+ if @namespace_map.key? namespace
215
+ # It's an attribute with a namespace we know
216
+ write_attribute(
217
+ @namespace_map[namespace] + ':' + local_name,
218
+ value
219
+ )
220
+ else
221
+ # We don't know the namespace, we must add it in-line
222
+ @adhoc_namespaces[namespace] = 'x' + (@adhoc_namespaces.size + 1).to_s unless @adhoc_namespaces.key?(namespace)
223
+
224
+ write_attribute_ns(
225
+ @adhoc_namespaces[namespace],
226
+ local_name,
227
+ namespace,
228
+ value
229
+ )
230
+ end
231
+ else
232
+ @writer.write_attribute(name, value)
233
+ end
234
+ end
235
+
236
+ # TODO: document this
237
+ def initialize
238
+ @adhoc_namespaces = {}
239
+ @namespaces_written = false
240
+ initialize_context_stack_attributes
241
+ end
242
+
243
+ # TODO: documentation
244
+ def open_memory
245
+ fail 'XML document already created' if @writer
246
+
247
+ @writer = ::LibXML::XML::Writer.string
248
+ end
249
+
250
+ # TODO: documentation
251
+ def output_memory
252
+ @writer.result
253
+ end
254
+
255
+ # TODO: documentation
256
+ def method_missing(name, *args)
257
+ @writer.send(name, *args)
258
+ end
259
+ end
260
+ end
261
+ end
@@ -0,0 +1,29 @@
1
+ module Tilia
2
+ module Xml
3
+ # Implementing the XmlDeserializable interface allows you to use a class as a
4
+ # deserializer for a specific element.
5
+ module XmlDeserializable
6
+ # The deserialize method is called during xml parsing.
7
+ #
8
+ # This method is called statictly, this is because in theory this method
9
+ # may be used as a type of constructor, or factory method.
10
+ #
11
+ # Often you want to return an instance of the current class, but you are
12
+ # free to return other data as well.
13
+ #
14
+ # You are responsible for advancing the reader to the next element. Not
15
+ # doing anything will result in a never-ending loop.
16
+ #
17
+ # If you just want to skip parsing for this element altogether, you can
18
+ # just call $reader->next();
19
+ #
20
+ # $reader->parseInnerTree() will parse the entire sub-tree, and advance to
21
+ # the next element.
22
+ #
23
+ # @param [Reader] _reader
24
+ # @return mixed
25
+ def xml_deserialize(_reader)
26
+ end
27
+ end
28
+ end
29
+ end