xml-simple 1.0.11 → 1.1.9
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/lib/xmlsimple.rb +144 -103
- metadata +51 -40
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 83fdfe764b21f66e184a245f9f908d2ed9678edd6d0734a33cac93030449c324
|
4
|
+
data.tar.gz: ecd9ac1ed12a0fb03f2b8fee924db65ec844dffda64f9553b76a626b2532635a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 652f1cbb3ef139e5450cdd2ad01fc4a22e23ec3174bd3d88c58b973f1c5c955673a365bbaa3e3c910ad60c190994e3e0e88fd4678af69d5981aadd2b0ecb019d
|
7
|
+
data.tar.gz: 40e7a0e3e6b0a42d178e8f6b782aca2e8a6d65ce87ab8aa06953d7dfe41b4ce115793018df2bab0b6238298d1c35ccdaa2fee9a0ea46ac927f2d2238d2f0146d
|
data/lib/xmlsimple.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# = XmlSimple
|
2
2
|
#
|
3
3
|
# Author:: Maik Schmidt <contact@maik-schmidt.de>
|
4
|
-
# Copyright:: Copyright (c) 2003-
|
4
|
+
# Copyright:: Copyright (c) 2003-2021 Maik Schmidt
|
5
5
|
# License:: Distributes under the same terms as Ruby.
|
6
6
|
#
|
7
7
|
require 'rexml/document'
|
@@ -11,7 +11,7 @@ require 'stringio'
|
|
11
11
|
class XmlSimple
|
12
12
|
include REXML
|
13
13
|
|
14
|
-
@@VERSION = '1.
|
14
|
+
@@VERSION = '1.1.9'
|
15
15
|
|
16
16
|
# A simple cache for XML documents that were already transformed
|
17
17
|
# by xml_in.
|
@@ -23,7 +23,7 @@ class XmlSimple
|
|
23
23
|
end
|
24
24
|
|
25
25
|
# Saves a data structure into a file.
|
26
|
-
#
|
26
|
+
#
|
27
27
|
# data::
|
28
28
|
# Data structure to be saved.
|
29
29
|
# filename::
|
@@ -121,12 +121,12 @@ class XmlSimple
|
|
121
121
|
# Create a "global" cache.
|
122
122
|
@@cache = Cache.new
|
123
123
|
|
124
|
-
# Creates and
|
125
|
-
#
|
124
|
+
# Creates and initializes a new XmlSimple object.
|
125
|
+
#
|
126
126
|
# defaults::
|
127
127
|
# Default values for options.
|
128
128
|
def initialize(defaults = nil)
|
129
|
-
unless defaults.nil? || defaults.
|
129
|
+
unless defaults.nil? || defaults.is_a?(Hash)
|
130
130
|
raise ArgumentError, "Options have to be a Hash."
|
131
131
|
end
|
132
132
|
@default_options = normalize_option_names(defaults, (KNOWN_OPTIONS['in'] + KNOWN_OPTIONS['out']).uniq)
|
@@ -143,7 +143,7 @@ class XmlSimple
|
|
143
143
|
# - filename: Tries to load and parse filename.
|
144
144
|
# - IO object: Reads from object until EOF is detected and parses result.
|
145
145
|
# - XML string: Parses string.
|
146
|
-
#
|
146
|
+
#
|
147
147
|
# options::
|
148
148
|
# Options to be used.
|
149
149
|
def xml_in(string = nil, options = nil)
|
@@ -151,7 +151,7 @@ class XmlSimple
|
|
151
151
|
|
152
152
|
# If no XML string or filename was supplied look for scriptname.xml.
|
153
153
|
if string.nil?
|
154
|
-
string = File::basename($0)
|
154
|
+
string = File::basename($0).dup
|
155
155
|
string.sub!(/\.[^.]+$/, '')
|
156
156
|
string += '.xml'
|
157
157
|
|
@@ -159,11 +159,11 @@ class XmlSimple
|
|
159
159
|
@options['searchpath'].unshift(directory) unless directory.nil?
|
160
160
|
end
|
161
161
|
|
162
|
-
if string.
|
162
|
+
if string.is_a?(String)
|
163
163
|
if string =~ /<.*?>/m
|
164
164
|
@doc = parse(string)
|
165
165
|
elsif string == '-'
|
166
|
-
@doc = parse($stdin.
|
166
|
+
@doc = parse($stdin.read)
|
167
167
|
else
|
168
168
|
filename = find_xml_file(string, @options['searchpath'])
|
169
169
|
|
@@ -182,13 +182,13 @@ class XmlSimple
|
|
182
182
|
return content if content
|
183
183
|
}
|
184
184
|
end
|
185
|
-
|
185
|
+
|
186
186
|
@doc = load_xml_file(filename)
|
187
187
|
end
|
188
|
-
elsif string.
|
189
|
-
@doc = parse(string.
|
188
|
+
elsif string.respond_to?(:read)
|
189
|
+
@doc = parse(string.read)
|
190
190
|
else
|
191
|
-
raise ArgumentError, "Could not parse object of type: <#{string.
|
191
|
+
raise ArgumentError, "Could not parse object of type: <#{string.class}>."
|
192
192
|
end
|
193
193
|
|
194
194
|
result = collapse(@doc.root)
|
@@ -202,7 +202,7 @@ class XmlSimple
|
|
202
202
|
xml_simple = XmlSimple.new
|
203
203
|
xml_simple.xml_in(string, options)
|
204
204
|
end
|
205
|
-
|
205
|
+
|
206
206
|
# Converts a data structure into an XML document.
|
207
207
|
#
|
208
208
|
# ref::
|
@@ -211,7 +211,7 @@ class XmlSimple
|
|
211
211
|
# Options to be used.
|
212
212
|
def xml_out(ref, options = nil)
|
213
213
|
handle_options('out', options)
|
214
|
-
if ref.
|
214
|
+
if ref.is_a?(Array)
|
215
215
|
ref = { @options['anonymoustag'] => ref }
|
216
216
|
end
|
217
217
|
|
@@ -222,7 +222,7 @@ class XmlSimple
|
|
222
222
|
@options['rootname'] = keys[0]
|
223
223
|
end
|
224
224
|
elsif @options['rootname'] == ''
|
225
|
-
if ref.
|
225
|
+
if ref.is_a?(Hash)
|
226
226
|
refsave = ref
|
227
227
|
ref = {}
|
228
228
|
refsave.each { |key, value|
|
@@ -258,21 +258,21 @@ class XmlSimple
|
|
258
258
|
xml_simple = XmlSimple.new
|
259
259
|
xml_simple.xml_out(hash, options)
|
260
260
|
end
|
261
|
-
|
261
|
+
|
262
262
|
private
|
263
263
|
|
264
264
|
# Declare options that are valid for xml_in and xml_out.
|
265
265
|
KNOWN_OPTIONS = {
|
266
266
|
'in' => %w(
|
267
|
-
keyattr keeproot forcecontent contentkey noattr
|
268
|
-
|
269
|
-
|
270
|
-
|
267
|
+
keyattr keeproot forcecontent contentkey noattr searchpath
|
268
|
+
forcearray suppressempty anonymoustag cache grouptags
|
269
|
+
normalisespace normalizespace variables varattr keytosymbol
|
270
|
+
attrtosymbol attrprefix conversions kebabtosnakecase
|
271
271
|
),
|
272
272
|
'out' => %w(
|
273
273
|
keyattr keeproot contentkey noattr rootname
|
274
274
|
xmldeclaration outputfile noescape suppressempty
|
275
|
-
anonymoustag indent grouptags noindent
|
275
|
+
anonymoustag indent grouptags noindent attrprefix selfclose
|
276
276
|
)
|
277
277
|
}
|
278
278
|
|
@@ -285,11 +285,13 @@ class XmlSimple
|
|
285
285
|
DEF_FORCE_ARRAY = true
|
286
286
|
DEF_INDENTATION = ' '
|
287
287
|
DEF_KEY_TO_SYMBOL = false
|
288
|
-
|
288
|
+
DEF_ATTR_TO_SYMBOL = false
|
289
|
+
DEF_KEBAB_TO_SNAKE = false
|
290
|
+
|
289
291
|
# Normalizes option names in a hash, i.e., turns all
|
290
292
|
# characters to lower case and removes all underscores.
|
291
|
-
# Additionally, this method checks
|
292
|
-
# was used and raises an according exception.
|
293
|
+
# Additionally, this method checks if an unknown option
|
294
|
+
# was used, and raises an according exception.
|
293
295
|
#
|
294
296
|
# options::
|
295
297
|
# Hash to be normalized.
|
@@ -299,18 +301,17 @@ class XmlSimple
|
|
299
301
|
return nil if options.nil?
|
300
302
|
result = Hash.new
|
301
303
|
options.each { |key, value|
|
302
|
-
lkey = key.downcase
|
303
|
-
lkey.gsub!(/_/, '')
|
304
|
+
lkey = key.to_s.downcase.gsub(/_/, '')
|
304
305
|
if !known_options.member?(lkey)
|
305
|
-
raise ArgumentError, "
|
306
|
+
raise ArgumentError, "Unrecognized option: #{lkey}."
|
306
307
|
end
|
307
308
|
result[lkey] = value
|
308
309
|
}
|
309
310
|
result
|
310
311
|
end
|
311
|
-
|
312
|
+
|
312
313
|
# Merges a set of options with the default options.
|
313
|
-
#
|
314
|
+
#
|
314
315
|
# direction::
|
315
316
|
# 'in': If options should be handled for xml_in.
|
316
317
|
# 'out': If options should be handled for xml_out.
|
@@ -319,7 +320,7 @@ class XmlSimple
|
|
319
320
|
def handle_options(direction, options)
|
320
321
|
@options = options || Hash.new
|
321
322
|
|
322
|
-
raise ArgumentError, "Options must be a Hash!" unless @options.
|
323
|
+
raise ArgumentError, "Options must be a Hash!" unless @options.is_a?(Hash)
|
323
324
|
|
324
325
|
unless KNOWN_OPTIONS.has_key?(direction)
|
325
326
|
raise ArgumentError, "Unknown direction: <#{direction}>."
|
@@ -354,6 +355,8 @@ class XmlSimple
|
|
354
355
|
|
355
356
|
@options['keytosymbol'] = DEF_KEY_TO_SYMBOL unless @options.has_key?('keytosymbol')
|
356
357
|
|
358
|
+
@options['attrtosymbol'] = DEF_ATTR_TO_SYMBOL unless @options.has_key?('attrtosymbol')
|
359
|
+
|
357
360
|
if @options.has_key?('contentkey')
|
358
361
|
if @options['contentkey'] =~ /^-(.*)$/
|
359
362
|
@options['contentkey'] = $1
|
@@ -369,7 +372,7 @@ class XmlSimple
|
|
369
372
|
@options['normalisespace'] = 0 if @options['normalisespace'].nil?
|
370
373
|
|
371
374
|
if @options.has_key?('searchpath')
|
372
|
-
unless @options['searchpath'].
|
375
|
+
unless @options['searchpath'].is_a?(Array)
|
373
376
|
@options['searchpath'] = [ @options['searchpath'] ]
|
374
377
|
end
|
375
378
|
else
|
@@ -394,13 +397,13 @@ class XmlSimple
|
|
394
397
|
if !scalar(@options['keyattr'])
|
395
398
|
# Convert keyattr => { elem => '+attr' }
|
396
399
|
# to keyattr => { elem => ['attr', '+'] }
|
397
|
-
if @options['keyattr'].
|
400
|
+
if @options['keyattr'].is_a?(Hash)
|
398
401
|
@options['keyattr'].each { |key, value|
|
399
402
|
if value =~ /^([-+])?(.*)$/
|
400
403
|
@options['keyattr'][key] = [$2, $1 ? $1 : '']
|
401
404
|
end
|
402
405
|
}
|
403
|
-
elsif !@options['keyattr'].
|
406
|
+
elsif !@options['keyattr'].is_a?(Array)
|
404
407
|
raise ArgumentError, "'keyattr' must be String, Hash, or Array!"
|
405
408
|
end
|
406
409
|
else
|
@@ -411,17 +414,17 @@ class XmlSimple
|
|
411
414
|
end
|
412
415
|
|
413
416
|
if @options.has_key?('forcearray')
|
414
|
-
if @options['forcearray'].
|
417
|
+
if @options['forcearray'].is_a?(Regexp)
|
415
418
|
@options['forcearray'] = [ @options['forcearray'] ]
|
416
419
|
end
|
417
420
|
|
418
|
-
if @options['forcearray'].
|
421
|
+
if @options['forcearray'].is_a?(Array)
|
419
422
|
force_list = @options['forcearray']
|
420
423
|
unless force_list.empty?
|
421
424
|
@options['forcearray'] = {}
|
422
425
|
force_list.each { |tag|
|
423
|
-
if tag.
|
424
|
-
unless @options['forcearray']['_regex'].
|
426
|
+
if tag.is_a?(Regexp)
|
427
|
+
unless @options['forcearray']['_regex'].is_a?(Array)
|
425
428
|
@options['forcearray']['_regex'] = []
|
426
429
|
end
|
427
430
|
@options['forcearray']['_regex'] << tag
|
@@ -439,11 +442,11 @@ class XmlSimple
|
|
439
442
|
@options['forcearray'] = DEF_FORCE_ARRAY
|
440
443
|
end
|
441
444
|
|
442
|
-
if @options.has_key?('grouptags') && !@options['grouptags'].
|
445
|
+
if @options.has_key?('grouptags') && !@options['grouptags'].is_a?(Hash)
|
443
446
|
raise ArgumentError, "Illegal value for 'GroupTags' option - expected a Hash."
|
444
447
|
end
|
445
448
|
|
446
|
-
if @options.has_key?('variables') && !@options['variables'].
|
449
|
+
if @options.has_key?('variables') && !@options['variables'].is_a?(Hash)
|
447
450
|
raise ArgumentError, "Illegal value for 'Variables' option - expected a Hash."
|
448
451
|
end
|
449
452
|
|
@@ -452,6 +455,8 @@ class XmlSimple
|
|
452
455
|
elsif @options.has_key?('varattr')
|
453
456
|
@_var_values = {}
|
454
457
|
end
|
458
|
+
|
459
|
+
@options['kebabtosnakecase'] = DEF_KEBAB_TO_SNAKE unless @options.has_key?('kebabtosnakecase')
|
455
460
|
end
|
456
461
|
|
457
462
|
# Actually converts an XML document element into a data structure.
|
@@ -480,7 +485,7 @@ class XmlSimple
|
|
480
485
|
result[@options['contentkey']] = content
|
481
486
|
end
|
482
487
|
elsif element.has_text? # i.e. it has only text.
|
483
|
-
return collapse_text_node(result, element)
|
488
|
+
return collapse_text_node(result, element) # calls merge, which converts
|
484
489
|
end
|
485
490
|
|
486
491
|
# Turn Arrays into Hashes if key fields present.
|
@@ -489,18 +494,21 @@ class XmlSimple
|
|
489
494
|
# Disintermediate grouped tags.
|
490
495
|
if @options.has_key?('grouptags')
|
491
496
|
result.each { |key, value|
|
492
|
-
|
497
|
+
# In results, key should already be converted
|
498
|
+
raise("Unconverted key '#{key}' found. Should be '#{kebab_to_snake_case key}'.") if (key != kebab_to_snake_case(key))
|
499
|
+
next unless (value.is_a?(Hash) && (value.size == 1))
|
493
500
|
child_key, child_value = value.to_a[0]
|
501
|
+
child_key = kebab_to_snake_case child_key # todo test whether necessary
|
494
502
|
if @options['grouptags'][key] == child_key
|
495
503
|
result[key] = child_value
|
496
504
|
end
|
497
505
|
}
|
498
506
|
end
|
499
|
-
|
500
|
-
# Fold
|
501
|
-
if count == 1
|
507
|
+
|
508
|
+
# Fold Hashes containing a single anonymous Array up into just the Array.
|
509
|
+
if count == 1
|
502
510
|
anonymoustag = @options['anonymoustag']
|
503
|
-
if result.has_key?(anonymoustag) && result[anonymoustag].
|
511
|
+
if result.has_key?(anonymoustag) && result[anonymoustag].is_a?(Array)
|
504
512
|
return result[anonymoustag]
|
505
513
|
end
|
506
514
|
end
|
@@ -538,16 +546,17 @@ class XmlSimple
|
|
538
546
|
end
|
539
547
|
|
540
548
|
# Folds all arrays in a Hash.
|
541
|
-
#
|
549
|
+
#
|
542
550
|
# hash::
|
543
551
|
# Hash to be folded.
|
544
552
|
def fold_arrays(hash)
|
545
553
|
fold_amount = 0
|
546
554
|
keyattr = @options['keyattr']
|
547
|
-
if (keyattr.
|
555
|
+
if (keyattr.is_a?(Array) || keyattr.is_a?(Hash))
|
548
556
|
hash.each { |key, value|
|
549
|
-
|
550
|
-
|
557
|
+
key = kebab_to_snake_case key
|
558
|
+
if value.is_a?(Array)
|
559
|
+
if keyattr.is_a?(Array)
|
551
560
|
hash[key] = fold_array(value)
|
552
561
|
else
|
553
562
|
hash[key] = fold_array_by_name(key, value)
|
@@ -568,13 +577,13 @@ class XmlSimple
|
|
568
577
|
def fold_array(array)
|
569
578
|
hash = Hash.new
|
570
579
|
array.each { |x|
|
571
|
-
return array unless x.
|
580
|
+
return array unless x.is_a?(Hash)
|
572
581
|
key_matched = false
|
573
582
|
@options['keyattr'].each { |key|
|
574
583
|
if x.has_key?(key)
|
575
584
|
key_matched = true
|
576
585
|
value = x[key]
|
577
|
-
return array if value.
|
586
|
+
return array if value.is_a?(Hash) || value.is_a?(Array)
|
578
587
|
value = normalise_space(value) if @options['normalisespace'] == 1
|
579
588
|
x.delete(key)
|
580
589
|
hash[value] = x
|
@@ -586,7 +595,7 @@ class XmlSimple
|
|
586
595
|
hash = collapse_content(hash) if @options['collapseagain']
|
587
596
|
hash
|
588
597
|
end
|
589
|
-
|
598
|
+
|
590
599
|
# Folds an Array to a Hash, if possible. Folding happens
|
591
600
|
# according to the content of keyattr, which has to be
|
592
601
|
# a Hash.
|
@@ -601,9 +610,9 @@ class XmlSimple
|
|
601
610
|
|
602
611
|
hash = Hash.new
|
603
612
|
array.each { |x|
|
604
|
-
if x.
|
613
|
+
if x.is_a?(Hash) && x.has_key?(key)
|
605
614
|
value = x[key]
|
606
|
-
return array if value.
|
615
|
+
return array if value.is_a?(Hash) || value.is_a?(Array)
|
607
616
|
value = normalise_space(value) if @options['normalisespace'] == 1
|
608
617
|
hash[value] = x
|
609
618
|
hash[value]["-#{key}"] = hash[value][key] if flag == '-'
|
@@ -624,12 +633,12 @@ class XmlSimple
|
|
624
633
|
def collapse_content(hash)
|
625
634
|
content_key = @options['contentkey']
|
626
635
|
hash.each_value { |value|
|
627
|
-
return hash unless value.
|
636
|
+
return hash unless value.is_a?(Hash) && value.size == 1 && value.has_key?(content_key)
|
628
637
|
hash.each_key { |key| hash[key] = hash[key][content_key] }
|
629
638
|
}
|
630
639
|
hash
|
631
640
|
end
|
632
|
-
|
641
|
+
|
633
642
|
# Adds a new key/value pair to an existing Hash. If the key to be added
|
634
643
|
# does already exist and the existing value associated with key is not
|
635
644
|
# an Array, it will be converted into an Array. Then the new value is
|
@@ -642,37 +651,42 @@ class XmlSimple
|
|
642
651
|
# value::
|
643
652
|
# Value to be associated with key.
|
644
653
|
def merge(hash, key, value)
|
645
|
-
|
654
|
+
key = kebab_to_snake_case key
|
655
|
+
if value.is_a?(String)
|
646
656
|
value = normalise_space(value) if @options['normalisespace'] == 2
|
647
657
|
|
658
|
+
if conv = @options['conversions'] and conv = conv.find {|c,_| c.match(key)} and conv = conv.at(1)
|
659
|
+
value = conv.call(value)
|
660
|
+
end
|
661
|
+
|
648
662
|
# do variable substitutions
|
649
663
|
unless @_var_values.nil? || @_var_values.empty?
|
650
664
|
value.gsub!(/\$\{(\w+)\}/) { |x| get_var($1) }
|
651
665
|
end
|
652
|
-
|
666
|
+
|
653
667
|
# look for variable definitions
|
654
668
|
if @options.has_key?('varattr')
|
655
|
-
varattr = @options['varattr']
|
669
|
+
varattr = kebab_to_snake_case @options['varattr']
|
656
670
|
if hash.has_key?(varattr)
|
657
671
|
set_var(hash[varattr], value)
|
658
672
|
end
|
659
673
|
end
|
660
674
|
end
|
661
|
-
|
675
|
+
|
662
676
|
#patch for converting keys to symbols
|
663
677
|
if @options.has_key?('keytosymbol')
|
664
678
|
if @options['keytosymbol'] == true
|
665
679
|
key = key.to_s.downcase.to_sym
|
666
680
|
end
|
667
681
|
end
|
668
|
-
|
682
|
+
|
669
683
|
if hash.has_key?(key)
|
670
|
-
if hash[key].
|
684
|
+
if hash[key].is_a?(Array)
|
671
685
|
hash[key] << value
|
672
686
|
else
|
673
687
|
hash[key] = [ hash[key], value ]
|
674
688
|
end
|
675
|
-
elsif value.
|
689
|
+
elsif value.is_a?(Array) # Handle anonymous arrays.
|
676
690
|
hash[key] = [ value ]
|
677
691
|
else
|
678
692
|
if force_array?(key)
|
@@ -683,21 +697,21 @@ class XmlSimple
|
|
683
697
|
end
|
684
698
|
hash
|
685
699
|
end
|
686
|
-
|
700
|
+
|
687
701
|
# Checks, if the 'forcearray' option has to be used for
|
688
702
|
# a certain key.
|
689
703
|
def force_array?(key)
|
690
704
|
return false if key == @options['contentkey']
|
691
705
|
return true if @options['forcearray'] == true
|
692
706
|
forcearray = @options['forcearray']
|
693
|
-
if forcearray.
|
694
|
-
return true if forcearray.has_key?(key)
|
707
|
+
if forcearray.is_a?(Hash)
|
708
|
+
return true if forcearray.has_key?(key)
|
695
709
|
return false unless forcearray.has_key?('_regex')
|
696
710
|
forcearray['_regex'].each { |x| return true if key =~ x }
|
697
711
|
end
|
698
712
|
return false
|
699
713
|
end
|
700
|
-
|
714
|
+
|
701
715
|
# Converts the attributes array of a document node into a Hash.
|
702
716
|
# Returns an empty Hash, if node has no attributes.
|
703
717
|
#
|
@@ -705,21 +719,28 @@ class XmlSimple
|
|
705
719
|
# Document node to extract attributes from.
|
706
720
|
def get_attributes(node)
|
707
721
|
attributes = {}
|
708
|
-
|
722
|
+
if @options['attrprefix']
|
723
|
+
node.attributes.each { |n,v| attributes["@" + kebab_to_snake_case(n)] = v }
|
724
|
+
elsif @options.has_key?('attrtosymbol') and @options['attrtosymbol'] == true
|
725
|
+
#patch for converting attribute names to symbols
|
726
|
+
node.attributes.each { |n,v| attributes[kebab_to_snake_case(n).to_sym] = v }
|
727
|
+
else
|
728
|
+
node.attributes.each { |n,v| attributes[kebab_to_snake_case(n)] = v }
|
729
|
+
end
|
730
|
+
|
709
731
|
attributes
|
710
732
|
end
|
711
|
-
|
733
|
+
|
712
734
|
# Determines, if a document element has mixed content.
|
713
735
|
#
|
714
736
|
# element::
|
715
737
|
# Document element to be checked.
|
716
738
|
def has_mixed_content?(element)
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
false
|
739
|
+
element.has_text? &&
|
740
|
+
element.has_elements? &&
|
741
|
+
!element.texts.join('').strip.empty?
|
721
742
|
end
|
722
|
-
|
743
|
+
|
723
744
|
# Called when a variable definition is encountered in the XML.
|
724
745
|
# A variable definition looks like
|
725
746
|
# <element attrname="name">value</element>
|
@@ -737,7 +758,7 @@ class XmlSimple
|
|
737
758
|
return "${#{name}}"
|
738
759
|
end
|
739
760
|
end
|
740
|
-
|
761
|
+
|
741
762
|
# Recurses through a data structure building up and returning an
|
742
763
|
# XML representation of that structure as a string.
|
743
764
|
#
|
@@ -765,12 +786,12 @@ class XmlSimple
|
|
765
786
|
end
|
766
787
|
|
767
788
|
# Unfold hash to array if possible.
|
768
|
-
if ref.
|
789
|
+
if ref.is_a?(Hash) && !ref.empty? && !@options['keyattr'].empty? && indent != ''
|
769
790
|
ref = hash_to_array(name, ref)
|
770
791
|
end
|
771
792
|
|
772
793
|
result = []
|
773
|
-
if ref.
|
794
|
+
if ref.is_a?(Hash)
|
774
795
|
# Reintermediate grouped values if applicable.
|
775
796
|
if @options.has_key?('grouptags')
|
776
797
|
ref.each { |key, value|
|
@@ -779,7 +800,7 @@ class XmlSimple
|
|
779
800
|
end
|
780
801
|
}
|
781
802
|
end
|
782
|
-
|
803
|
+
|
783
804
|
nested = []
|
784
805
|
text_content = nil
|
785
806
|
if named
|
@@ -788,7 +809,7 @@ class XmlSimple
|
|
788
809
|
|
789
810
|
if !ref.empty?
|
790
811
|
ref.each { |key, value|
|
791
|
-
next if !key.nil? && key[0, 1] == '-'
|
812
|
+
next if !key.nil? && key.to_s[0, 1] == '-'
|
792
813
|
if value.nil?
|
793
814
|
unless @options.has_key?('suppressempty') && @options['suppressempty'].nil?
|
794
815
|
raise ArgumentError, "Use of uninitialized value!"
|
@@ -796,7 +817,13 @@ class XmlSimple
|
|
796
817
|
value = {}
|
797
818
|
end
|
798
819
|
|
799
|
-
|
820
|
+
# Check for the '@' attribute prefix to allow separation of attributes and elements
|
821
|
+
|
822
|
+
if (@options['noattr'] ||
|
823
|
+
(@options['attrprefix'] && !(key =~ /^@(.*)/)) ||
|
824
|
+
!scalar(value)
|
825
|
+
) &&
|
826
|
+
key != @options['contentkey']
|
800
827
|
nested << value_to_xml(value, key, indent + @options['indent'])
|
801
828
|
else
|
802
829
|
value = value.to_s
|
@@ -804,11 +831,11 @@ class XmlSimple
|
|
804
831
|
if key == @options['contentkey']
|
805
832
|
text_content = value
|
806
833
|
else
|
807
|
-
result << ' ' << key << '="' << value << '"'
|
834
|
+
result << ' ' << ($1||key) << '="' << value << '"'
|
808
835
|
end
|
809
836
|
end
|
810
837
|
}
|
811
|
-
|
838
|
+
elsif !@options['selfclose']
|
812
839
|
text_content = ''
|
813
840
|
end
|
814
841
|
|
@@ -831,13 +858,13 @@ class XmlSimple
|
|
831
858
|
else
|
832
859
|
result << ' />' << nl
|
833
860
|
end
|
834
|
-
elsif ref.
|
861
|
+
elsif ref.is_a?(Array)
|
835
862
|
ref.each { |value|
|
836
863
|
if scalar(value)
|
837
864
|
result << indent << '<' << name << '>'
|
838
865
|
result << (@options['noescape'] ? value.to_s : escape_value(value.to_s))
|
839
866
|
result << '</' << name << '>' << nl
|
840
|
-
elsif value.
|
867
|
+
elsif value.is_a?(Hash)
|
841
868
|
result << value_to_xml(value, name, indent)
|
842
869
|
else
|
843
870
|
result << indent << '<' << name << '>' << nl
|
@@ -852,31 +879,31 @@ class XmlSimple
|
|
852
879
|
@ancestors.pop if !scalar(ref)
|
853
880
|
result.join('')
|
854
881
|
end
|
855
|
-
|
882
|
+
|
856
883
|
# Checks, if a certain value is a "scalar" value. Whatever
|
857
884
|
# that will be in Ruby ... ;-)
|
858
|
-
#
|
885
|
+
#
|
859
886
|
# value::
|
860
887
|
# Value to be checked.
|
861
888
|
def scalar(value)
|
862
|
-
return false if value.
|
889
|
+
return false if value.is_a?(Hash) || value.is_a?(Array)
|
863
890
|
return true
|
864
891
|
end
|
865
892
|
|
866
893
|
# Attempts to unfold a hash of hashes into an array of hashes. Returns
|
867
894
|
# a reference to th array on success or the original hash, if unfolding
|
868
895
|
# is not possible.
|
869
|
-
#
|
896
|
+
#
|
870
897
|
# parent::
|
871
|
-
#
|
898
|
+
#
|
872
899
|
# hashref::
|
873
900
|
# Reference to the hash to be unfolded.
|
874
901
|
def hash_to_array(parent, hashref)
|
875
902
|
arrayref = []
|
876
903
|
hashref.each { |key, value|
|
877
|
-
return hashref unless value.
|
904
|
+
return hashref unless value.is_a?(Hash)
|
878
905
|
|
879
|
-
if @options['keyattr'].
|
906
|
+
if @options['keyattr'].is_a?(Hash)
|
880
907
|
return hashref unless @options['keyattr'].has_key?(parent)
|
881
908
|
arrayref << { @options['keyattr'][parent][0] => key }.update(value)
|
882
909
|
else
|
@@ -885,7 +912,7 @@ class XmlSimple
|
|
885
912
|
}
|
886
913
|
arrayref
|
887
914
|
end
|
888
|
-
|
915
|
+
|
889
916
|
# Replaces XML markup characters by their external entities.
|
890
917
|
#
|
891
918
|
# data::
|
@@ -893,7 +920,7 @@ class XmlSimple
|
|
893
920
|
def escape_value(data)
|
894
921
|
Text::normalize(data)
|
895
922
|
end
|
896
|
-
|
923
|
+
|
897
924
|
# Removes leading and trailing whitespace and sequences of
|
898
925
|
# whitespaces from a string.
|
899
926
|
#
|
@@ -918,7 +945,7 @@ class XmlSimple
|
|
918
945
|
return value.nil?
|
919
946
|
end
|
920
947
|
end
|
921
|
-
|
948
|
+
|
922
949
|
# Converts a document node into a String.
|
923
950
|
# If the node could not be converted into a String
|
924
951
|
# for any reason, default will be returned.
|
@@ -928,11 +955,11 @@ class XmlSimple
|
|
928
955
|
# default::
|
929
956
|
# Value to be returned, if node could not be converted.
|
930
957
|
def node_to_text(node, default = nil)
|
931
|
-
if node.
|
958
|
+
if node.is_a?(REXML::Element)
|
932
959
|
node.texts.map { |t| t.value }.join('')
|
933
|
-
elsif node.
|
960
|
+
elsif node.is_a?(REXML::Attribute)
|
934
961
|
node.value.nil? ? default : node.value.strip
|
935
|
-
elsif node.
|
962
|
+
elsif node.is_a?(REXML::Text)
|
936
963
|
node.value.strip
|
937
964
|
else
|
938
965
|
default
|
@@ -951,7 +978,7 @@ class XmlSimple
|
|
951
978
|
def parse(xml_string)
|
952
979
|
Document.new(xml_string)
|
953
980
|
end
|
954
|
-
|
981
|
+
|
955
982
|
# Searches in a list of paths for a certain file. Returns
|
956
983
|
# the full path to the file, if it could be found. Otherwise,
|
957
984
|
# an exception will be raised.
|
@@ -978,20 +1005,20 @@ class XmlSimple
|
|
978
1005
|
end
|
979
1006
|
raise ArgumentError, "Could not find <#{filename}> in <#{searchpath.join(':')}>"
|
980
1007
|
end
|
981
|
-
|
1008
|
+
|
982
1009
|
# Loads and parses an XML configuration file.
|
983
1010
|
#
|
984
1011
|
# filename::
|
985
1012
|
# Name of the configuration file to be loaded.
|
986
1013
|
#
|
987
1014
|
# The following exceptions may be raised:
|
988
|
-
#
|
1015
|
+
#
|
989
1016
|
# Errno::ENOENT::
|
990
1017
|
# If the specified file does not exist.
|
991
1018
|
# REXML::ParseException::
|
992
1019
|
# If the specified file is not wellformed.
|
993
1020
|
def load_xml_file(filename)
|
994
|
-
parse(
|
1021
|
+
parse(IO::read(filename))
|
995
1022
|
end
|
996
1023
|
|
997
1024
|
# Caches the data belonging to a certain file.
|
@@ -1016,6 +1043,20 @@ class XmlSimple
|
|
1016
1043
|
}
|
1017
1044
|
end
|
1018
1045
|
end
|
1046
|
+
|
1047
|
+
# Substitutes underscores for hyphens if the KebabToSnakeCase option is selected. For when you don't
|
1048
|
+
# want to refer to keys by hash[:'my-key'] but instead as hash[:my_key]
|
1049
|
+
#
|
1050
|
+
# key::
|
1051
|
+
# Key to be converted.
|
1052
|
+
def kebab_to_snake_case(key)
|
1053
|
+
return key unless (@options['kebabtosnakecase'])
|
1054
|
+
|
1055
|
+
is_symbol = key.is_a? Symbol
|
1056
|
+
key = key.to_s.gsub(/-/, '_')
|
1057
|
+
key = key.to_sym if is_symbol
|
1058
|
+
key
|
1059
|
+
end
|
1019
1060
|
end
|
1020
1061
|
|
1021
1062
|
# vim:sw=2
|
metadata
CHANGED
@@ -1,46 +1,57 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
2
|
-
rubygems_version: 0.9.0
|
3
|
-
specification_version: 1
|
1
|
+
--- !ruby/object:Gem::Specification
|
4
2
|
name: xml-simple
|
5
|
-
version: !ruby/object:Gem::Version
|
6
|
-
version: 1.
|
7
|
-
date: 2007-03-12 00:00:00 +01:00
|
8
|
-
summary: A very simple API for XML processing.
|
9
|
-
require_paths:
|
10
|
-
- lib
|
11
|
-
email: contact@maik-schmidt.de
|
12
|
-
homepage: http://xml-simple.rubyforge.org
|
13
|
-
rubyforge_project: xml-simple
|
14
|
-
description:
|
15
|
-
autorequire: xmlsimple
|
16
|
-
default_executable:
|
17
|
-
bindir: bin
|
18
|
-
has_rdoc: false
|
19
|
-
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
-
requirements:
|
21
|
-
- - ">"
|
22
|
-
- !ruby/object:Gem::Version
|
23
|
-
version: 0.0.0
|
24
|
-
version:
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.1.9
|
25
5
|
platform: ruby
|
26
|
-
|
27
|
-
cert_chain:
|
28
|
-
post_install_message:
|
29
|
-
authors:
|
6
|
+
authors:
|
30
7
|
- Maik Schmidt
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-01-18 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rexml
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
description:
|
28
|
+
email: contact@maik-schmidt.de
|
39
29
|
executables: []
|
40
|
-
|
41
30
|
extensions: []
|
42
|
-
|
31
|
+
extra_rdoc_files: []
|
32
|
+
files:
|
33
|
+
- lib/xmlsimple.rb
|
34
|
+
homepage: https://github.com/maik/xml-simple
|
35
|
+
licenses:
|
36
|
+
- MIT
|
37
|
+
metadata: {}
|
38
|
+
post_install_message:
|
39
|
+
rdoc_options: []
|
40
|
+
require_paths:
|
41
|
+
- lib
|
42
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: '0'
|
43
52
|
requirements: []
|
44
|
-
|
45
|
-
|
46
|
-
|
53
|
+
rubygems_version: 3.1.2
|
54
|
+
signing_key:
|
55
|
+
specification_version: 4
|
56
|
+
summary: A simple API for XML processing.
|
57
|
+
test_files: []
|