├── .devcontainer
└── devcontainer.json
├── .github
├── dependabot.yml
└── workflows
│ └── test.yml
├── .gitignore
├── .rspec
├── CHANGELOG.md
├── Gemfile
├── LICENSE
├── README.md
├── Rakefile
├── benchmark
├── benchmark.rb
└── soap_response.xml
├── lib
├── nori.rb
└── nori
│ ├── core_ext.rb
│ ├── core_ext
│ └── hash.rb
│ ├── parser
│ ├── nokogiri.rb
│ └── rexml.rb
│ ├── string_io_file.rb
│ ├── string_utils.rb
│ ├── string_with_attributes.rb
│ ├── version.rb
│ └── xml_utility_node.rb
├── nori.gemspec
└── spec
├── nori
├── api_spec.rb
├── core_ext
│ └── hash_spec.rb
├── nori_spec.rb
└── string_utils_spec.rb
└── spec_helper.rb
/.devcontainer/devcontainer.json:
--------------------------------------------------------------------------------
1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the
2 | // README at: https://github.com/devcontainers/templates/tree/main/src/ruby
3 | {
4 | "name": "Ruby",
5 | // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
6 | "image": "mcr.microsoft.com/devcontainers/ruby:0-3-bullseye"
7 |
8 | // Features to add to the dev container. More info: https://containers.dev/features.
9 | // "features": {},
10 |
11 | // Use 'forwardPorts' to make a list of ports inside the container available locally.
12 | // "forwardPorts": [],
13 |
14 | // Configure tool-specific properties.
15 | // "customizations": {},
16 |
17 | // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
18 | // "remoteUser": "root"
19 | }
20 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for more information:
4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5 | # https://containers.dev/guide/dependabot
6 |
7 | version: 2
8 | updates:
9 | - package-ecosystem: "devcontainers"
10 | directory: "/"
11 | schedule:
12 | interval: weekly
13 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | build:
7 | runs-on: ubuntu-latest
8 |
9 | strategy:
10 | matrix:
11 | ruby-version:
12 | - '3.0'
13 | - '3.1'
14 | - '3.2'
15 | - '3.3'
16 | - 'head'
17 | - jruby-9.4.5.0
18 | steps:
19 | - uses: actions/checkout@v4
20 | - name: Set up Ruby ${{ matrix.ruby-version }}
21 | uses: ruby/setup-ruby@v1
22 | with:
23 | ruby-version: ${{ matrix.ruby-version }}
24 | bundler-cache: true # runs 'bundle install' and caches installed gems automatically
25 | - name: Run tests
26 | run: bundle exec rake
27 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | doc
3 | coverage
4 | *~
5 | *.gem
6 | .bundle
7 | Gemfile.lock
8 | /.idea
9 |
--------------------------------------------------------------------------------
/.rspec:
--------------------------------------------------------------------------------
1 | --colour
2 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # 2.7.1 (2024-07-28)
2 |
3 | * Stop monkey-patching String with #snakecase by @mchu in https://github.com/savonrb/nori/pull/102
4 |
5 | # 2.7.0 (2024-02-13)
6 |
7 | * Added support for ruby 3.1, 3.2, 3.3. Dropped support for ruby 2.7 and below.
8 | * Feature: `Nori::Parser` has a new option, `:scrub_xml`, which defaults to true, for scrubbing invalid characters ([#72](https://github.com/savonrb/nori/pull/72)). This should allow documents containing invalid characters to still be parsed.
9 | * Fix: REXML parser changes `<` inside CDATA to `<` ([#94](https://github.com/savonrb/nori/pull/94))
10 | * Change: `Object#blank?` is no longer patched in.
11 |
12 | # 2.6.0 (2015-05-06)
13 |
14 | * Feature: [#69](https://github.com/savonrb/nori/pull/69) Add option to convert empty tags to a value other than nil.
15 |
16 | # 2.5.0 (2015-03-31)
17 |
18 | * Formally drop support for ruby 1.8.7. Installing Nori from rubygems for that version should no longer attempt to install versions that will not work.
19 | * BREAKING CHANGE: Newlines are now preserved when present in the value of inner text nodes. See the example below:
20 |
21 | before:
22 |
23 | ```
24 | Nori.new.parse("\n<embedded>\n<one></one>\n<two></two>\n<embedded>\n")
25 | => {"outer"=>""}
26 | ```
27 |
28 | after:
29 | ```
30 | Nori.new.parse("\n<embedded>\n<one></one>\n<two></two>\n<embedded>\n")
31 | => {"outer"=>"\n\n\n\n"}
32 | ```
33 |
34 | # 2.4.0 (2014-04-19)
35 |
36 | * Change: Dropped support for ruby 1.8, rubinius and ree
37 |
38 | * Feature: Added `:convert_attributes` feature similar to `:convert_tags_to`
39 |
40 | * Feature: Added `:convert_dashes_to_underscore` option
41 |
42 | # 2.3.0 (2013-07-26)
43 |
44 | * Change: `Nori#find` now ignores namespace prefixes in Hash keys it is searching through.
45 |
46 | * Fix: Limited Nokogiri to < 1.6, because v1.6 dropped support for Ruby 1.8.
47 |
48 | # 2.2.0 (2013-04-25)
49 |
50 | * Feature: [#42](https://github.com/savonrb/nori/pull/42) adds the `:delete_namespace_attributes`
51 | option to remove namespace attributes like `xmlns:*` or `xsi:*`.
52 |
53 | # 2.1.0 (2013-04-21)
54 |
55 | * Feature: Added `Nori.hash_key` and `Nori#find` to work with Hash keys generated by Nori.
56 | Original issue: [savonrb/savon#393](https://github.com/savonrb/savon/pull/393)
57 |
58 | # 2.0.4 (2013-02-26)
59 |
60 | * Fix: [#37](https://github.com/savonrb/nori/issues/37) special characters
61 | problem on Ruby 1.9.3-p392.
62 |
63 | # 2.0.3 (2013-01-10)
64 |
65 | * Fix for remote code execution bug. For more in-depth information, read about the
66 | recent [Rails hotfix](https://groups.google.com/forum/?fromgroups=#!topic/rubyonrails-security/61bkgvnSGTQ).
67 | Please make sure to upgrade now!
68 |
69 | # 2.0.2 (YANKED)
70 |
71 | * Yanked because of a problem with XML that starts with an instruction tag.
72 |
73 | # 2.0.1 (YANKED)
74 |
75 | * Yanked because of a problem with XML that starts with an instruction tag.
76 |
77 | # 2.0.0 (2012-12-12)
78 |
79 | Please make sure to read the updated README for how to use the new version.
80 |
81 | * Change: Nori now defaults to use the Nokogiri parser.
82 |
83 | * Refactoring: Changed the `Nori` module to a class. This might cause problems if you
84 | included the `Nori` module somewhere in your application. This use case was removed
85 | for overall simplicity.
86 |
87 | * Refactoring: Changed the interface to remove any global state. The global configuration
88 | is gone and replaced with simple options to be passed to `Nori.new`.
89 |
90 | ``` ruby
91 | parser = Nori.new(strip_namespaces: true)
92 | parser.parse(xml)
93 | ```
94 |
95 | * Refactoring: Removed the `Nori::Parser` module methods. After refactoring the rest,
96 | there was only a single method left for this module and that was moved to `Nori`.
97 |
98 | * Fix: [#16](https://github.com/savonrb/nori/issues/16) strip XML passed to Nori.
99 |
100 | ## 1.1.5 (2013-03-03)
101 |
102 | * Fix: [#37](https://github.com/savonrb/nori/issues/37) special characters
103 | problem on Ruby 1.9.3-p392.
104 |
105 | ## 1.1.4 (2013-01-10)
106 |
107 | * Fix for remote code execution bug. For more in-depth information, read about the
108 | recent [Rails hotfix](https://groups.google.com/forum/?fromgroups=#!topic/rubyonrails-security/61bkgvnSGTQ).
109 | Please make sure to upgrade now!
110 |
111 | ## 1.1.3 (2012-07-12)
112 |
113 | * Fix: Merged [pull request 21](https://github.com/savonrb/nori/pull/21) to fix an
114 | issue with date/time/datetime regexes not matching positive time zone offsets and
115 | datetime strings with seconds.
116 |
117 | ## 1.1.2 (2012-06-30)
118 |
119 | * Fix: Reverted `Object#xml_attributes` feature which is planned for version 2.0.
120 |
121 | ## 1.1.1 (2012-06-29) - yanked
122 |
123 | * Fix: Merged [pull request 17](https://github.com/savonrb/nori/pull/17) for improved
124 | xs:time/xs:date/xs:dateTime regular expression matchers.
125 |
126 | ## 1.1.0 (2012-02-17)
127 |
128 | * Improvement: Merged [pull request 9](https://github.com/savonrb/nori/pull/9) to
129 | allow multiple configurations of Nori.
130 |
131 | * Fix: Merged [pull request 10](https://github.com/savonrb/nori/pull/10) to handle
132 | date/time parsing errors. Fixes a couple of similar error reports.
133 |
134 | ## 1.0.2 (2011-07-04)
135 |
136 | * Fix: When specifying a custom formula to convert tags, XML attributes were ignored.
137 | Now, a formula is applied to both XML tags and attributes.
138 |
139 | ## 1.0.1 (2011-06-21)
140 |
141 | * Fix: Make sure to always load both StringWithAttributes and StringIOFile
142 | to prevent NameError's.
143 |
144 | ## 1.0.0 (2011-06-20)
145 |
146 | * Notice: As of v1.0.0, Nori will follow [Semantic Versioning](http://semver.org).
147 |
148 | * Feature: Added somewhat advanced typecasting:
149 |
150 | What this means:
151 |
152 | * "true" and "false" are converted to TrueClass and FalseClass
153 | * Strings matching an xs:time, xs:date and xs:dateTime are converted
154 | to Time, Date and DateTime objects.
155 |
156 | You can disable this feature via:
157 |
158 | Nori.advanced_typecasting = false
159 |
160 | * Feature: Added an option to strip the namespaces from every tag.
161 | This feature might raise problems and is therefore disabled by default.
162 |
163 | Nori.strip_namespaces = true
164 |
165 | * Feature: Added an option to specify a custom formula to convert tags.
166 | Here's an example:
167 |
168 | Nori.configure do |config|
169 | config.convert_tags_to { |tag| tag.snake_case.to_sym }
170 | end
171 |
172 | xml = 'active'
173 | parse(xml).should ## { :user_response => { :account_status => "active" }
174 |
175 | ## 0.2.4 (2011-06-21)
176 |
177 | * Fix: backported fixes from v1.0.1
178 |
179 | ## 0.2.3 (2011-05-26)
180 |
181 | * Fix: Use extended core classes StringWithAttributes and StringIOFile instead of
182 | creating singletons to prevent serialization problems.
183 |
184 | ## 0.2.2 (2011-05-16)
185 |
186 | * Fix: namespaced xs:nil values should be nil objects.
187 |
188 | ## 0.2.1 (2011-05-15)
189 |
190 | * Fix: Changed XML attributes converted to Hash keys to be prefixed with an @-sign.
191 | This avoids problems with attributes and child nodes having the same name.
192 |
193 |
194 | true
195 | 76737
196 |
197 |
198 | is now translated to:
199 |
200 | { "multiRef" => { "@id" => "id1", "id" => "76737", "approved" => "true" } }
201 |
202 | ## 0.2.0 (2011-04-30)
203 |
204 | * Removed JSON from the original Crack basis
205 | * Fixed a problem with Object#blank?
206 | * Added swappable parsers
207 | * Added a Nokogiri parser with you can switch to via:
208 |
209 | Nori.parser = :nokogiri
210 |
211 | ## 0.1.7 2010-02-19
212 | * 1 minor patch
213 | * Added patch from @purp for ISO 8601 date/time format
214 |
215 | ## 0.1.6 2010-01-31
216 | * 1 minor patch
217 | * Added Crack::VERSION constant - http://weblog.rubyonrails.org/2009/9/1/gem-packaging-best-practices
218 |
219 | ## 0.1.5 2010-01-27
220 | * 1 minor patch
221 | * Strings that begin with dates shouldn't be parsed as such (sandro)
222 |
223 | ## 0.1.3 2009-06-22
224 | * 1 minor patch
225 | * Parsing a text node with attributes stores them in the attributes method (tamalw)
226 |
227 | ## 0.1.2 2009-04-21
228 | * 2 minor patches
229 | * Correct unnormalization of attribute values (der-flo)
230 | * Fix error in parsing YAML in the case where a hash value ends with backslashes, and there are subsequent values in the hash (deadprogrammer)
231 |
232 | ## 0.1.1 2009-03-31
233 | * 1 minor patch
234 | * Parsing empty or blank xml now returns empty hash instead of raising error.
235 |
236 | ## 0.1.0 2009-03-28
237 | * Initial release.
238 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 | gemspec
3 |
4 | if RUBY_VERSION >= "3"
5 | gem "rexml", "~> 3.2"
6 | end
7 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2011 Daniel Harrington
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Nori
2 | ====
3 |
4 | [](https://github.com/savonrb/nori/actions/workflows/test.yml)
5 | [](http://badge.fury.io/rb/nori)
6 | [](https://codeclimate.com/github/savonrb/nori)
7 |
8 | Really simple XML parsing ripped from Crack, which ripped it from Merb.
9 |
10 | Nori supports pluggable parsers and ships with both REXML and Nokogiri implementations.
11 | It defaults to Nokogiri since v2.0.0, but you can change it to use REXML via:
12 |
13 | ``` ruby
14 | Nori.new(:parser => :rexml) # or :nokogiri
15 | ```
16 |
17 | Make sure Nokogiri is in your LOAD_PATH when parsing XML, because Nori tries to load it when it's needed.
18 |
19 | # Examples
20 |
21 | ```ruby
22 | Nori.new.parse("This is the content")
23 | # => {"tag"=>"This is the content"}
24 |
25 | Nori.new.parse('')
26 | #=> {"foo"=>nil}
27 |
28 | Nori.new.parse('')
29 | #=> {}
30 |
31 | Nori.new.parse('')
32 | #=> {"foo"=>{"@bar"=>"baz"}}
33 |
34 | Nori.new.parse('Content')
35 | #=> {"foo"=>"Content"}
36 | ```
37 |
38 | ## Nori::StringWithAttributes
39 |
40 | You can access a string node's attributes via `attributes`.
41 |
42 | ```ruby
43 | result = Nori.new.parse('Content')
44 | #=> {"foo"=>"Content"}
45 |
46 | result["foo"].class
47 | # => Nori::StringWithAttributes
48 |
49 | result["foo"].attributes
50 | # => {"bar"=>"baz"}
51 | ```
52 |
53 | ## advanced_typecasting
54 |
55 | Nori can automatically convert string values to `TrueClass`, `FalseClass`, `Time`, `Date`, and `DateTime`:
56 |
57 | ```ruby
58 | # "true" and "false" String values are converted to `TrueClass` and `FalseClass`.
59 | Nori.new.parse("true")
60 | # => {"value"=>true}
61 |
62 | # String values matching xs:time, xs:date and xs:dateTime are converted to `Time`, `Date` and `DateTime` objects.
63 | Nori.new.parse("09:33:55.7Z")
64 | # => {"value"=>2022-09-29 09:33:55.7 UTC
65 |
66 | # disable with advanced_typecasting: false
67 | Nori.new(advanced_typecasting: false).parse("true")
68 | # => {"value"=>"true"}
69 |
70 | ```
71 |
72 | ## strip_namespaces
73 |
74 | Nori can strip the namespaces from your XML tags. This feature is disabled by default.
75 |
76 | ``` ruby
77 | Nori.new.parse('')
78 | # => {"soap:Envelope"=>{"@xmlns:soap"=>"http://schemas.xmlsoap.org/soap/envelope/"}}
79 |
80 | Nori.new(:strip_namespaces => true).parse('')
81 | # => {"Envelope"=>{"@xmlns:soap"=>"http://schemas.xmlsoap.org/soap/envelope/"}}
82 | ```
83 |
84 | ## convert_tags_to
85 |
86 | Nori lets you specify a custom formula to convert XML tags to Hash keys using `convert_tags_to`.
87 |
88 | ``` ruby
89 | Nori.new.parse('active')
90 | # => {"userResponse"=>{"accountStatus"=>"active"}}
91 |
92 | parser = Nori.new(:convert_tags_to => lambda { |tag| Nori::StringUtils.snakecase(tag).to_sym })
93 | parser.parse('active')
94 | # => {:user_response=>{:account_status=>"active"}}
95 | ```
96 |
97 | ## convert_dashes_to_underscores
98 |
99 | By default, Nori will automatically convert dashes in tag names to underscores.
100 |
101 | ```ruby
102 | Nori.new.parse('foo bar')
103 | # => {"any_tag"=>"foo bar"}
104 |
105 | # disable with convert_dashes_to_underscores
106 | parser = Nori.new(:convert_dashes_to_underscores => false)
107 | parser.parse('foo bar')
108 | # => {"any-tag"=>"foo bar"}
109 | ```
110 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require "bundler/gem_tasks"
2 |
3 | desc "Benchmark Nori parsers"
4 | task :benchmark do
5 | require "benchmark/benchmark"
6 | end
7 |
8 | require "rspec/core/rake_task"
9 | RSpec::Core::RakeTask.new
10 |
11 | task :default => :spec
12 | task :test => :spec
13 |
--------------------------------------------------------------------------------
/benchmark/benchmark.rb:
--------------------------------------------------------------------------------
1 | $:.push File.expand_path("../../lib", __FILE__)
2 | require "nori"
3 |
4 | require "benchmark"
5 |
6 | Benchmark.bm 30 do |x|
7 |
8 | num = 500
9 | xml = File.read File.expand_path("../soap_response.xml", __FILE__)
10 |
11 | x.report "rexml parser" do
12 | num.times { Nori.new(parser: :rexml).parse xml }
13 | end
14 |
15 | x.report "nokogiri parser" do
16 | num.times { Nori.new(parser: :nokogiri).parse xml }
17 | end
18 |
19 | end
20 |
--------------------------------------------------------------------------------
/benchmark/soap_response.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 | PALMA DE MALLORCA
108 | Spain Schengen
109 | Son San Juan
110 | 11 km
111 | 08.02.10
112 | 08.02.10
113 | Passengers
114 | 1
115 | Disembarking:
116 | -1
117 | 2
118 | 0
119 | Baby buggies are delivered at aircraft door or stairs.
120 | 2008-09-01T00:00:00+02:00
121 | -1
122 | 0
123 | PMI
124 |
125 |
126 | PALMA DE MALLORCA
127 | Spain Schengen
128 | Son San Juan
129 | 11 km
130 | 08.02.10
131 | 08.02.10
132 | Aircraft
133 | 2
134 | Turnaround:
135 | -1
136 | 5.30
137 | 0
138 | Station applies EST process.
139 | 2009-05-05T00:00:00+02:00
140 | -1
141 | -1
142 | PMI
143 |
144 |
145 | PALMA DE MALLORCA
146 | Spain Schengen
147 | Son San Juan
148 | 11 km
149 | 08.02.10
150 | 08.02.10
151 | General
152 | 4
153 | Addresses:
154 | -1
155 | 8.50
156 | 1
157 |
158 | Station Manager:
159 | YYYYYYYY XXXXXXX
160 | PMIKXXX
161 | Tel.:+34 971 xxx xxx
162 | Mobile:+ 34 600 46 xx xx
163 |
164 | 2010-02-08T00:00:00+01:00
165 | -1
166 | -1
167 | PMI
168 |
169 |
170 | PALMA DE MALLORCA
171 | Spain Schengen
172 | Son San Juan
173 | 11 km
174 | 08.02.10
175 | 08.02.10
176 | General
177 | 4
178 | Addresses:
179 | -1
180 | 8.50
181 | 2
182 |
183 | Handling Agent:
184 | xxxxxxx Airport Services
185 | Operations
186 | PMIIxxx
187 | Tel +34 971 xxx xxx
188 | Passenger Services
189 | PMIPXXX
190 | Tel : +34 971 xxx xxx
191 |
192 | 2010-02-08T00:00:00+01:00
193 | -1
194 | -1
195 | PMI
196 |
197 |
198 | PALMA DE MALLORCA
199 | Spain Schengen
200 | Son San Juan
201 | 11 km
202 | 08.02.10
203 | 08.02.10
204 | General
205 | 4
206 | Announcements:
207 | -1
208 | 11
209 | 1
210 | Prerecorded Spanish announcements available.
211 | 2009-12-30T00:00:00+01:00
212 | -1
213 | 0
214 | PMI
215 |
216 |
217 | PALMA DE MALLORCA
218 | Spain Schengen
219 | Son San Juan
220 | 11 km
221 | 08.02.10
222 | 08.02.10
223 | General
224 | 4
225 | Life jackets / DEMO:
226 | -1
227 | 12
228 | 0
229 |
230 | Infant life jackets to be distributed.
231 | DEMO with life jackets.
232 |
233 | 2002-07-24T00:00:00+02:00
234 | -1
235 | 0
236 | PMI
237 |
238 |
239 | PALMA DE MALLORCA
240 | Spain Schengen
241 | Son San Juan
242 | 11 km
243 | 08.02.10
244 | 08.02.10
245 | Catering
246 | 5
247 | General:
248 | 0
249 | 1
250 | 1
251 |
252 | LSG XXX XXXX
253 | Tel.: +34 971 xxx xxx or xxx xxx
254 | Sita: PMIAXXX
255 |
256 | 2005-06-01T00:00:00+02:00
257 | -1
258 | 0
259 | PMI
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
--------------------------------------------------------------------------------
/lib/nori.rb:
--------------------------------------------------------------------------------
1 | require "nori/version"
2 | require "nori/core_ext"
3 | require "nori/xml_utility_node"
4 |
5 | class Nori
6 |
7 | def self.hash_key(name, options = {})
8 | name = name.tr("-", "_") if options[:convert_dashes_to_underscores]
9 | name = name.split(":").last if options[:strip_namespaces]
10 | name = options[:convert_tags_to].call(name) if options[:convert_tags_to].respond_to? :call
11 | name
12 | end
13 |
14 | PARSERS = { :rexml => "REXML", :nokogiri => "Nokogiri" }
15 |
16 | def initialize(options = {})
17 | defaults = {
18 | :strip_namespaces => false,
19 | :delete_namespace_attributes => false,
20 | :convert_tags_to => nil,
21 | :convert_attributes_to => nil,
22 | :empty_tag_value => nil,
23 | :advanced_typecasting => true,
24 | :convert_dashes_to_underscores => true,
25 | :scrub_xml => true,
26 | :parser => :nokogiri
27 | }
28 |
29 | validate_options! defaults.keys, options.keys
30 | @options = defaults.merge(options)
31 | end
32 |
33 | def find(hash, *path)
34 | return hash if path.empty?
35 |
36 | key = path.shift
37 | key = self.class.hash_key(key, @options)
38 |
39 | value = find_value(hash, key)
40 | find(value, *path) if value
41 | end
42 |
43 | def parse(xml)
44 | cleaned_xml = scrub_xml(xml).strip
45 | return {} if cleaned_xml.empty?
46 |
47 | parser = load_parser @options[:parser]
48 | parser.parse(cleaned_xml, @options)
49 | end
50 |
51 | private
52 | def load_parser(parser)
53 | require "nori/parser/#{parser}"
54 | Parser.const_get PARSERS[parser]
55 | end
56 |
57 | # Expects a +block+ which receives a tag to convert.
58 | # Accepts +nil+ for a reset to the default behavior of not converting tags.
59 | def convert_tags_to(reset = nil, &block)
60 | @convert_tag = reset || block
61 | end
62 |
63 | def validate_options!(available_options, options)
64 | spurious_options = options - available_options
65 |
66 | unless spurious_options.empty?
67 | raise ArgumentError, "Spurious options: #{spurious_options.inspect}\n" \
68 | "Available options are: #{available_options.inspect}"
69 | end
70 | end
71 |
72 | def find_value(hash, key)
73 | hash.each do |k, v|
74 | key_without_namespace = k.to_s.split(':').last
75 | return v if key_without_namespace == key.to_s
76 | end
77 |
78 | nil
79 | end
80 |
81 | def scrub_xml(string)
82 | if @options[:scrub_xml]
83 | if string.respond_to? :scrub
84 | string.scrub
85 | else
86 | if string.valid_encoding?
87 | string
88 | else
89 | enc = string.encoding
90 | mid_enc = (["UTF-8", "UTF-16BE"].map { |e| Encoding.find(e) } - [enc]).first
91 | string.encode(mid_enc, undef: :replace, invalid: :replace).encode(enc)
92 | end
93 | end
94 | else
95 | string
96 | end
97 | end
98 |
99 | end
100 |
--------------------------------------------------------------------------------
/lib/nori/core_ext.rb:
--------------------------------------------------------------------------------
1 | require "nori/string_utils"
2 | require "nori/core_ext/hash"
3 |
--------------------------------------------------------------------------------
/lib/nori/core_ext/hash.rb:
--------------------------------------------------------------------------------
1 | require "uri"
2 |
3 | class Nori
4 | module CoreExt
5 | module Hash
6 |
7 | # @param key