Class: Yadtfp::Parsers::Ox

Inherits:
Object
  • Object
show all
Defined in:
lib/yadtfp/parsers/ox.rb

Instance Method Summary collapse

Instance Method Details

#diff(left, right) ⇒ Object

Generates array of difference hashes between left and right.

The difference hash contains the following keys: type: Type of difference. Either c for change, a for append or d for delete. path: Path needs to be in the format described in the documentation of ::Ox::Element#locate. lvalue: Left value rvalue: Right value

Method loads xml document from left and right. Method parameters left and right both must be a ::Ox::Document, each of which can be result of parse method.

Method returns [] if both left or right are nil or both are equal. Method returns right translated into difference hash if left is nil. Method returns left translated into difference hash if right is nil.



71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/yadtfp/parsers/ox.rb', line 71

def diff(left, right)
  return [] if left == right || (left.nil? && right.nil?)


  left = flat_h(left)
  right = flat_h(right)


  return right.values if left.nil? && right.is_a?(::Hash)
  return left.values if right.nil? && left.is_a?(::Hash)


  return flat_h_diff(left, right)
end

#filter(doc, path = Yadtfp::Configuration.instance.filter) ⇒ Object

Raises:

  • (ArgumentError)


44
45
46
47
48
# File 'lib/yadtfp/parsers/ox.rb', line 44

def filter(doc, path = Yadtfp::Configuration.instance.filter)
  raise ArgumentError, "Document cannot be nil" if doc.nil?

  doc.locate(path)
end

#flat_h(element) ⇒ Object

flat_h: Flat hash

Returns hash of xml document. Traverses xml document recursively and generates a flat hash.

Each element is converted to a hash where key is path to the node, attribute, comment or CDATA and value is the string or nil value held by the node, attribute, comment or CDATA

Example:

Bar # => { '/xml/id' => 'root', '/xml/@name' => 'Foo', '/xml' => 'Bar' }

Raises:



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/yadtfp/parsers/ox.rb', line 103

def flat_h(element)
  raise UnsupportedError, "Multiple root elements are not allowed" if element.is_a?(::Array)


  # Nothing to process
  return nil if element.nil?


  # Result hash
  result = {}



  # Attributes
  attrs = process_attributes(element) || {}

  attrs.map do |key, value|
    path = element.value.to_s != '' ? "/#{element.value}/#{key}" : "/#{key}"
    result[path] = value
  end





  # Nodes
  if !element.is_a?(::Ox::Comment)

    if element.nodes.length.zero?
      path = "/#{element.value}"
      result[path] = nil if attrs.length.zero?
    end




    element.nodes.each do |node|

      if node.is_a?(::String)

        path = "/#{element.value}"

        if result.key?(path)
          result[path] = [ result[path] ] if !result[path].is_a?(::Array)
          result[path] << node
        else
          result[path] = node
        end

      elsif node.is_a?(::Ox::Comment)

        path = element.value.to_s != '' ? "/#{element.value}/comment()" : '/comment()'
        result[path] = process_comments(element)

      elsif node.is_a?(::Ox::CData)

        path = element.value.to_s != '' ? "/#{element.value}/cdata()" : '/cdata()'
        result[path] = process_cdata(element)

      else

        n = flat_h(node)


        n.each do |key, value|

          path = element.value.to_s == '' ? "#{key}" : "/#{element.value}#{key}"

          if result.key?(path)

            result[path] = [ result[path] ] if !result[path].is_a?(::Array)
            result[path] = [ result[path] ] if key.end_with?('comment()') || key.end_with?('cdata()')
            result[path] << value

          else

            result[path] = value

          end

        end

      end

    end

  end

  result

end

#flat_h_diff(left, right) ⇒ Object

Returns array of difference hashes for left and right flat hashes.

The resultant array consists of difference hashes in the form of:

{ type: type, path: path, lvalue: lvalue, rvalue: rvalue }

where:

type - Type of difference, either c, a or d path - Path to the node, attribute, cdata, comment. lvalue - Value at path in left rvalue - Value at path in right

Three possible types of differences are: c - When both left and right contain different values for same path a - Append values to left at the specified path. Occurs when path is not present in left. d - Delete values from left at the specified path. Occurs when path is not present in right.

Returns nil if left and right are equal.



221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
# File 'lib/yadtfp/parsers/ox.rb', line 221

def flat_h_diff(left, right)
  return nil if left == right

  diff_hash = ( left.keys | right.keys ).inject({}) do |hash, key|
    type = 'c'
    type = 'd' if !right.key?(key) && left.key?(key)
    type = 'a' if !left.key?(key) && right.key?(key)

    if left[key] != right[key]

      if left[key].is_a?(::Array) && right[key].is_a?(::Array)

        type = 'd' if left[key].length > right[key].length
        type = 'a' if left[key].length < right[key].length
        type = 'c' if (left[key] - right[key]).length >= 1 && (right[key] - left[key]).length >= 1


        if (left[key] - right[key]).empty? && (right[key] - left[key]).empty?
          hash[key] = { type: type, path: key, lvalue: left[key].uniq, rvalue: nil } if type == 'd'
          hash[key] = { type: type, path: key, lvalue: nil, rvalue: right[key].uniq } if type == 'a'

        else

          hash[key] = { type: type, path: key, lvalue: left[key] - right[key], rvalue: nil } if type == 'd'
          hash[key] = { type: type, path: key, lvalue: nil, rvalue: right[key] - left[key] } if type == 'a'
          hash[key] = { type: type, path: key, lvalue: (left[key] - right[key]), rvalue: (right[key] - left[key]) } if type == 'c'
        end

      else

        hash[key] = { type: type, path: key, lvalue: left[key], rvalue: right[key] }

      end

    end

    hash
  end

  diff_hash.values
end

#parse(xml) ⇒ Object

Parses xml into a ::Ox::Document and returns the same ::Ox::Document

Parameter xml is either a file path or a xml string. Method adds xml processing instruction with version: '1.0' and encoding: 'UTF-8'

Method returns empty document ::Ox::Document with processing instruction if supplied xml is nil or empty.



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/yadtfp/parsers/ox.rb', line 19

def parse(xml)
  doc = ::Ox::Document.new(version: '1.0', encoding: 'UTF-8')
  return doc if xml.nil? || xml.empty?


  file = ::Pathname.new(xml).absolute? ? xml : File.expand_path(xml)
  if File.exists?(file)
    parsed = ::Ox.load_file(file, node: :generic)
  else
    parsed = ::Ox.parse(xml)
  end
  parsed_doc = doc.dup << parsed if parsed.is_a?(::Ox::Element)


  # Apply filter
  filtered = filter(parsed_doc) if parsed_doc.is_a?(::Ox::Element)
  filtered.each { |f| doc << f } if filtered.is_a?(::Array)

  doc
end

#process_attributes(node) ⇒ Object

Process node attributes Returns hash of node attributes applying @ prefix to every attribute name

Returns nil if node does not contain any attributes



314
315
316
317
318
319
320
321
322
323
324
# File 'lib/yadtfp/parsers/ox.rb', line 314

def process_attributes(node)
  return nil if !node.respond_to?(:attributes)

  attributes = node.attributes.dup.inject({}) do |hash, (key, value)|
    hash["@#{key}"] = value
    hash
  end

  return attributes if attributes.length >= 1
  nil
end

#process_cdata(element) ⇒ Object

Process element cdata

Returns array of cdata Returns nil if element does not contain any cdata markup.



293
294
295
296
297
298
299
300
301
302
303
# File 'lib/yadtfp/parsers/ox.rb', line 293

def process_cdata(element)
  return if !element.respond_to?(:nodes)

  cdata = element.nodes.dup.inject([]) do |result, element|
    result << element.value if element.is_a?(::Ox::CData) && !element.value.empty?
    result
  end

  return cdata if cdata.length >= 1
  nil
end

#process_comments(element) ⇒ Object

Process element comments

Returns array of comments Returns nil if element does not contain any comment nodes.



272
273
274
275
276
277
278
279
280
281
282
# File 'lib/yadtfp/parsers/ox.rb', line 272

def process_comments(element)
  return if !element.respond_to?(:nodes)

  comments = element.nodes.dup.inject([]) do |result, element|
    result << element.value if element.is_a?(::Ox::Comment) && !element.value.empty?
    result
  end

  return comments if comments.length >= 1
  nil
end