rxerces 0.1.0 → 0.3.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 +4 -4
- checksums.yaml.gz.sig +4 -0
- data/CHANGES.md +21 -0
- data/Gemfile +7 -0
- data/README.md +6 -2
- data/Rakefile +43 -0
- data/certs/djberg96_pub.pem +26 -0
- data/examples/basic_usage.rb +75 -0
- data/examples/schema_example.rb +107 -0
- data/examples/simple_example.rb +34 -0
- data/examples/xpath_example.rb +108 -0
- data/ext/rxerces/rxerces.cpp +623 -1
- data/lib/rxerces/nokogiri.rb +34 -0
- data/lib/rxerces/version.rb +1 -1
- data/lib/rxerces.rb +0 -31
- data/rxerces.gemspec +33 -0
- data/spec/document_spec.rb +101 -0
- data/spec/element_spec.rb +25 -0
- data/spec/node_spec.rb +472 -0
- data/spec/nodeset_spec.rb +92 -0
- data/spec/nokogiri_compatibility_spec.rb +167 -0
- data/spec/rxerces_shared.rb +10 -0
- data/spec/rxerces_spec.rb +23 -0
- data/spec/schema_spec.rb +76 -0
- data/spec/spec_helper.rb +17 -0
- data/spec/xpath_spec.rb +164 -0
- data.tar.gz.sig +0 -0
- metadata +71 -9
- metadata.gz.sig +2 -0
- data/lib/rxerces/rxerces.bundle +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8a6f91adbbf73a000b638acc525461a8e0a06ce847bff4f8fd48ad5faf117483
|
|
4
|
+
data.tar.gz: ce10fe12e10294c5012624263cdbc05e4d751c7f62cad3647b2d73f88b19b8ab
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c70510870068a501df10218e42c2eee838df962588929e0731d7e37eb250a80f3e875f77646a254681b976f487435fa531aca4144c1efc39ba22c6ec79c722fc
|
|
7
|
+
data.tar.gz: 47cacc7b927ff6ea7a5c206cd24d2d44fb52bc74ebdb314d05fa67d3bacd8f228113d7053f759626f70acde116ddcfffa194969c85a46a6a8e7e5850f906f36e
|
checksums.yaml.gz.sig
ADDED
data/CHANGES.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
## 0.3.0 - 14-Dec-2025
|
|
2
|
+
* Added Node#parent.
|
|
3
|
+
* Added Element#attributes.
|
|
4
|
+
* Added Node#next_node and Node#next_sibling.
|
|
5
|
+
* Added Node#previous_node and Node#previous_sibling.
|
|
6
|
+
* Added Element#add_child.
|
|
7
|
+
* Added Node#remove and Node#unlink.
|
|
8
|
+
* Added Document#create_element.
|
|
9
|
+
* Added Element#inner_xml and Element#inner_html.
|
|
10
|
+
* Added Node#path.
|
|
11
|
+
* Added Node#blank?
|
|
12
|
+
|
|
13
|
+
## 0.2.0 - 13-Dec-2025
|
|
14
|
+
* The nokogiri compatibility layer is now optional.
|
|
15
|
+
* Fixed up the gemspec with real values instead of the AI generated junk.
|
|
16
|
+
* Updated the Rakefile a bit, reworked some of the tasks.
|
|
17
|
+
* Minor spec updates.
|
|
18
|
+
* Added my cert.
|
|
19
|
+
|
|
20
|
+
## 0.1.0 - 12-Dec-2025
|
|
21
|
+
* Initial release.
|
data/Gemfile
ADDED
data/README.md
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
[](https://github.com/djberg96/rxerces/actions/workflows/ci.yml)
|
|
2
|
+
|
|
1
3
|
# RXerces
|
|
2
4
|
|
|
3
5
|
A Ruby XML library with a Nokogiri-compatible API, powered by Apache Xerces-C instead of libxml2.
|
|
@@ -73,10 +75,10 @@ puts root.name # => "root"
|
|
|
73
75
|
|
|
74
76
|
### Nokogiri Compatibility
|
|
75
77
|
|
|
76
|
-
RXerces provides
|
|
78
|
+
RXerces provides optional Nokogiri compatibility. Require `rxerces/nokogiri` to enable drop-in replacement:
|
|
77
79
|
|
|
78
80
|
```ruby
|
|
79
|
-
require 'rxerces'
|
|
81
|
+
require 'rxerces/nokogiri'
|
|
80
82
|
|
|
81
83
|
# Use Nokogiri syntax
|
|
82
84
|
doc = Nokogiri.XML('<root><child>text</child></root>')
|
|
@@ -86,6 +88,8 @@ puts doc.root.name # => "root"
|
|
|
86
88
|
Nokogiri::XML::Document == RXerces::XML::Document # => true
|
|
87
89
|
```
|
|
88
90
|
|
|
91
|
+
**Note:** If you don't need Nokogiri compatibility, just `require 'rxerces'` and use the `RXerces` module directly.
|
|
92
|
+
|
|
89
93
|
### Working with Nodes
|
|
90
94
|
|
|
91
95
|
```ruby
|
data/Rakefile
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require "rake/extensiontask"
|
|
2
|
+
require "rspec/core/rake_task"
|
|
3
|
+
require "rake/clean"
|
|
4
|
+
require "rbconfig"
|
|
5
|
+
include RbConfig
|
|
6
|
+
|
|
7
|
+
CLEAN.include(
|
|
8
|
+
'**/*.gem', # Gem files
|
|
9
|
+
'**/*.rbc', # Rubinius
|
|
10
|
+
'**/*.o', # C object file
|
|
11
|
+
'**/*.log', # Ruby extension build log
|
|
12
|
+
'**/*.lock', # Gemfile.lock
|
|
13
|
+
'**/Makefile', # C Makefile
|
|
14
|
+
'**/conftest.dSYM', # OS X build directory
|
|
15
|
+
"**/*.#{CONFIG['DLEXT']}" # C shared object
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
namespace :gem do
|
|
19
|
+
desc "Create the sys-uname gem"
|
|
20
|
+
task :create => [:clean] do
|
|
21
|
+
require 'rubygems/package'
|
|
22
|
+
spec = Gem::Specification.load('rxerces.gemspec')
|
|
23
|
+
spec.signing_key = File.join(Dir.home, '.ssh', 'gem-private_key.pem')
|
|
24
|
+
Gem::Package.build(spec)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
desc "Install the sys-uname gem"
|
|
28
|
+
task :install => [:create] do
|
|
29
|
+
file = Dir["*.gem"].first
|
|
30
|
+
sh "gem install #{file}"
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
Rake::ExtensionTask.new("rxerces") do |ext|
|
|
35
|
+
ext.lib_dir = "lib/rxerces"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
|
39
|
+
t.verbose = false
|
|
40
|
+
t.rspec_opts = '-f documentation -w'
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
task default: [:compile, :spec]
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
|
2
|
+
MIIEcDCCAtigAwIBAgIBATANBgkqhkiG9w0BAQsFADA/MREwDwYDVQQDDAhkamJl
|
|
3
|
+
cmc5NjEVMBMGCgmSJomT8ixkARkWBWdtYWlsMRMwEQYKCZImiZPyLGQBGRYDY29t
|
|
4
|
+
MB4XDTE4MDMxODE1MjIwN1oXDTI4MDMxNTE1MjIwN1owPzERMA8GA1UEAwwIZGpi
|
|
5
|
+
ZXJnOTYxFTATBgoJkiaJk/IsZAEZFgVnbWFpbDETMBEGCgmSJomT8ixkARkWA2Nv
|
|
6
|
+
bTCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBALgfaroVM6CI06cxr0/h
|
|
7
|
+
A+j+pc8fgpRgBVmHFaFunq28GPC3IvW7Nvc3Y8SnAW7pP1EQIbhlwRIaQzJ93/yj
|
|
8
|
+
u95KpkP7tA9erypnV7dpzBkzNlX14ACaFD/6pHoXoe2ltBxk3CCyyzx70mTqJpph
|
|
9
|
+
75IB03ni9a8yqn8pmse+s83bFJOAqddSj009sGPcQO+QOWiNxqYv1n5EHcvj2ebO
|
|
10
|
+
6hN7YTmhx7aSia4qL/quc4DlIaGMWoAhvML7u1fmo53CYxkKskfN8MOecq2vfEmL
|
|
11
|
+
iLu+SsVVEAufMDDFMXMJlvDsviolUSGMSNRTujkyCcJoXKYYxZSNtIiyd9etI0X3
|
|
12
|
+
ctu0uhrFyrMZXCedutvXNjUolD5r9KGBFSWH1R9u2I3n3SAyFF2yzv/7idQHLJJq
|
|
13
|
+
74BMnx0FIq6fCpu5slAipvxZ3ZkZpEXZFr3cIBtO1gFvQWW7E/Y3ijliWJS1GQFq
|
|
14
|
+
058qERadHGu1yu1dojmFRo6W2KZvY9al2yIlbkpDrD5MYQIDAQABo3cwdTAJBgNV
|
|
15
|
+
HRMEAjAAMAsGA1UdDwQEAwIEsDAdBgNVHQ4EFgQUFZsMapgzJimzsbaBG2Tm8j5e
|
|
16
|
+
AzgwHQYDVR0RBBYwFIESZGpiZXJnOTZAZ21haWwuY29tMB0GA1UdEgQWMBSBEmRq
|
|
17
|
+
YmVyZzk2QGdtYWlsLmNvbTANBgkqhkiG9w0BAQsFAAOCAYEAW2tnYixXQtKxgGXq
|
|
18
|
+
/3iSWG2bLwvxS4go3srO+aRXZHrFUMlJ5W0mCxl03aazxxKTsVVpZD8QZxvK91OQ
|
|
19
|
+
h9zr9JBYqCLcCVbr8SkmYCi/laxIZxsNE5YI8cC8vvlLI7AMgSfPSnn/Epq1GjGY
|
|
20
|
+
6L1iRcEDtanGCIvjqlCXO9+BmsnCfEVehqZkQHeYczA03tpOWb6pon2wzvMKSsKH
|
|
21
|
+
ks0ApVdstSLz1kzzAqem/uHdG9FyXdbTAwH1G4ZPv69sQAFAOCgAqYmdnzedsQtE
|
|
22
|
+
1LQfaQrx0twO+CZJPcRLEESjq8ScQxWRRkfuh2VeR7cEU7L7KqT10mtUwrvw7APf
|
|
23
|
+
DYoeCY9KyjIBjQXfbj2ke5u1hZj94Fsq9FfbEQg8ygCgwThnmkTrrKEiMSs3alYR
|
|
24
|
+
ORVCZpRuCPpmC8qmqxUnARDArzucjaclkxjLWvCVHeFa9UP7K3Nl9oTjJNv+7/jM
|
|
25
|
+
WZs4eecIcUc4tKdHxcAJ0MO/Dkqq7hGaiHpwKY76wQ1+8xAh
|
|
26
|
+
-----END CERTIFICATE-----
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
require 'rxerces'
|
|
2
|
+
require 'rxerces/nokogiri'
|
|
3
|
+
|
|
4
|
+
puts "=== RXerces Basic Usage Example ===\n\n"
|
|
5
|
+
|
|
6
|
+
# Parse XML
|
|
7
|
+
xml = <<-XML
|
|
8
|
+
<library name="City Library">
|
|
9
|
+
<book id="1" isbn="978-0451524935">
|
|
10
|
+
<title>1984</title>
|
|
11
|
+
<author>George Orwell</author>
|
|
12
|
+
<year>1949</year>
|
|
13
|
+
</book>
|
|
14
|
+
<book id="2" isbn="978-0060850524">
|
|
15
|
+
<title>Brave New World</title>
|
|
16
|
+
<author>Aldous Huxley</author>
|
|
17
|
+
<year>1932</year>
|
|
18
|
+
</book>
|
|
19
|
+
</library>
|
|
20
|
+
XML
|
|
21
|
+
|
|
22
|
+
puts "1. Parsing XML document..."
|
|
23
|
+
doc = RXerces.XML(xml)
|
|
24
|
+
puts " ✓ Document parsed successfully\n\n"
|
|
25
|
+
|
|
26
|
+
# Access root element
|
|
27
|
+
puts "2. Accessing root element..."
|
|
28
|
+
root = doc.root
|
|
29
|
+
puts " Root element name: #{root.name}"
|
|
30
|
+
puts " Library name: #{root['name']}\n\n"
|
|
31
|
+
|
|
32
|
+
# Navigate children
|
|
33
|
+
puts "3. Navigating child elements..."
|
|
34
|
+
books = root.children.select { |n| n.is_a?(RXerces::XML::Element) }
|
|
35
|
+
puts " Found #{books.length} books:\n\n"
|
|
36
|
+
|
|
37
|
+
books.each do |book|
|
|
38
|
+
title = book.children.find { |n| n.name == 'title' }
|
|
39
|
+
author = book.children.find { |n| n.name == 'author' }
|
|
40
|
+
year = book.children.find { |n| n.name == 'year' }
|
|
41
|
+
|
|
42
|
+
puts " Book ##{book['id']}:"
|
|
43
|
+
puts " Title: #{title.text.strip}"
|
|
44
|
+
puts " Author: #{author.text.strip}"
|
|
45
|
+
puts " Year: #{year.text.strip}"
|
|
46
|
+
puts " ISBN: #{book['isbn']}"
|
|
47
|
+
puts
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Modify document
|
|
51
|
+
puts "4. Modifying the document..."
|
|
52
|
+
first_book = books.first
|
|
53
|
+
first_book['rating'] = '5 stars'
|
|
54
|
+
title = first_book.children.find { |n| n.name == 'title' }
|
|
55
|
+
puts " Added rating to first book: #{first_book['rating']}"
|
|
56
|
+
puts " First book title: #{title.text.strip}\n\n"
|
|
57
|
+
|
|
58
|
+
# Serialize back to XML
|
|
59
|
+
puts "5. Serializing to XML..."
|
|
60
|
+
xml_output = doc.to_xml
|
|
61
|
+
puts " ✓ Document serialized successfully"
|
|
62
|
+
puts "\n Output preview:"
|
|
63
|
+
puts " " + xml_output.lines.first(3).join(" ")
|
|
64
|
+
puts " ...\n\n"
|
|
65
|
+
|
|
66
|
+
# Nokogiri compatibility
|
|
67
|
+
puts "6. Testing Nokogiri compatibility..."
|
|
68
|
+
nokogiri_doc = Nokogiri.XML('<test><item>Hello World</item></test>')
|
|
69
|
+
puts " Parsed with Nokogiri.XML: #{nokogiri_doc.root.name}"
|
|
70
|
+
item = nokogiri_doc.root.children.find { |n| n.is_a?(Nokogiri::XML::Element) }
|
|
71
|
+
puts " Item text: #{item.text}"
|
|
72
|
+
puts " Document class: #{nokogiri_doc.class}"
|
|
73
|
+
puts " ✓ Nokogiri compatibility confirmed\n\n"
|
|
74
|
+
|
|
75
|
+
puts "=== Example Complete ==="
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
require_relative '../lib/rxerces'
|
|
3
|
+
|
|
4
|
+
# Define an XSD schema
|
|
5
|
+
schema_xsd = <<~XSD
|
|
6
|
+
<?xml version="1.0"?>
|
|
7
|
+
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
|
8
|
+
<xs:element name="person">
|
|
9
|
+
<xs:complexType>
|
|
10
|
+
<xs:sequence>
|
|
11
|
+
<xs:element name="name" type="xs:string"/>
|
|
12
|
+
<xs:element name="age" type="xs:integer"/>
|
|
13
|
+
<xs:element name="email" type="xs:string"/>
|
|
14
|
+
</xs:sequence>
|
|
15
|
+
</xs:complexType>
|
|
16
|
+
</xs:element>
|
|
17
|
+
</xs:schema>
|
|
18
|
+
XSD
|
|
19
|
+
|
|
20
|
+
puts "=" * 60
|
|
21
|
+
puts "RXerces Schema Validation Example"
|
|
22
|
+
puts "=" * 60
|
|
23
|
+
|
|
24
|
+
# Create a schema from the XSD
|
|
25
|
+
puts "\nCreating schema from XSD..."
|
|
26
|
+
schema = RXerces::XML::Schema.from_string(schema_xsd)
|
|
27
|
+
puts "✓ Schema created successfully"
|
|
28
|
+
|
|
29
|
+
# Test 1: Valid document
|
|
30
|
+
puts "\n" + "-" * 60
|
|
31
|
+
puts "Test 1: Validating a VALID document"
|
|
32
|
+
puts "-" * 60
|
|
33
|
+
|
|
34
|
+
valid_xml = <<~XML
|
|
35
|
+
<?xml version="1.0"?>
|
|
36
|
+
<person>
|
|
37
|
+
<name>John Doe</name>
|
|
38
|
+
<age>30</age>
|
|
39
|
+
<email>john@example.com</email>
|
|
40
|
+
</person>
|
|
41
|
+
XML
|
|
42
|
+
|
|
43
|
+
doc = RXerces::XML::Document.parse(valid_xml)
|
|
44
|
+
errors = doc.validate(schema)
|
|
45
|
+
if errors.empty?
|
|
46
|
+
puts "✓ Document is VALID (no validation errors)"
|
|
47
|
+
else
|
|
48
|
+
puts "✗ Document has validation errors:"
|
|
49
|
+
errors.each { |error| puts " - #{error}" }
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Test 2: Invalid document (missing required element)
|
|
53
|
+
puts "\n" + "-" * 60
|
|
54
|
+
puts "Test 2: Validating an INVALID document (missing 'email')"
|
|
55
|
+
puts "-" * 60
|
|
56
|
+
|
|
57
|
+
invalid_xml = <<~XML
|
|
58
|
+
<?xml version="1.0"?>
|
|
59
|
+
<person>
|
|
60
|
+
<name>Jane Doe</name>
|
|
61
|
+
<age>25</age>
|
|
62
|
+
</person>
|
|
63
|
+
XML
|
|
64
|
+
|
|
65
|
+
doc2 = RXerces::XML::Document.parse(invalid_xml)
|
|
66
|
+
errors2 = doc2.validate(schema)
|
|
67
|
+
if errors2.empty?
|
|
68
|
+
puts "✓ Document is VALID (no validation errors)"
|
|
69
|
+
else
|
|
70
|
+
puts "✗ Document has validation errors (as expected):"
|
|
71
|
+
errors2.each { |error| puts " - #{error}" }
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Test 3: Invalid document (wrong type)
|
|
75
|
+
puts "\n" + "-" * 60
|
|
76
|
+
puts "Test 3: Validating an INVALID document (wrong type for 'age')"
|
|
77
|
+
puts "-" * 60
|
|
78
|
+
|
|
79
|
+
invalid_xml2 = <<~XML
|
|
80
|
+
<?xml version="1.0"?>
|
|
81
|
+
<person>
|
|
82
|
+
<name>Bob Smith</name>
|
|
83
|
+
<age>not-a-number</age>
|
|
84
|
+
<email>bob@example.com</email>
|
|
85
|
+
</person>
|
|
86
|
+
XML
|
|
87
|
+
|
|
88
|
+
doc3 = RXerces::XML::Document.parse(invalid_xml2)
|
|
89
|
+
errors3 = doc3.validate(schema)
|
|
90
|
+
if errors3.empty?
|
|
91
|
+
puts "✓ Document is VALID (no validation errors)"
|
|
92
|
+
else
|
|
93
|
+
puts "✗ Document has validation errors (as expected):"
|
|
94
|
+
errors3.each { |error| puts " - #{error}" }
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# You can also create a schema from a document
|
|
98
|
+
puts "\n" + "-" * 60
|
|
99
|
+
puts "Creating schema from a Document object..."
|
|
100
|
+
puts "-" * 60
|
|
101
|
+
schema_doc = RXerces::XML::Document.parse(schema_xsd)
|
|
102
|
+
schema2 = RXerces::XML::Schema.from_document(schema_doc)
|
|
103
|
+
puts "✓ Schema created from Document"
|
|
104
|
+
|
|
105
|
+
puts "\n" + "=" * 60
|
|
106
|
+
puts "Full XSD validation is now implemented using Xerces-C!"
|
|
107
|
+
puts "=" * 60
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
require 'bundler/setup'
|
|
3
|
+
require 'rxerces'
|
|
4
|
+
|
|
5
|
+
puts "=== RXerces Simple Example (No Nokogiri) ===\n\n"
|
|
6
|
+
|
|
7
|
+
xml = <<-XML
|
|
8
|
+
<bookstore>
|
|
9
|
+
<book>
|
|
10
|
+
<title>1984</title>
|
|
11
|
+
<author>George Orwell</author>
|
|
12
|
+
</book>
|
|
13
|
+
</bookstore>
|
|
14
|
+
XML
|
|
15
|
+
|
|
16
|
+
# Parse using RXerces directly
|
|
17
|
+
doc = RXerces.XML(xml)
|
|
18
|
+
|
|
19
|
+
puts "Document parsed successfully!"
|
|
20
|
+
puts "Root element: #{doc.root.name}"
|
|
21
|
+
puts
|
|
22
|
+
|
|
23
|
+
# Find the book
|
|
24
|
+
book = doc.root.children.find { |n| n.is_a?(RXerces::XML::Element) }
|
|
25
|
+
title = book.children.find { |n| n.name == 'title' }
|
|
26
|
+
author = book.children.find { |n| n.name == 'author' }
|
|
27
|
+
|
|
28
|
+
puts "Book found:"
|
|
29
|
+
puts " Title: #{title.text.strip}"
|
|
30
|
+
puts " Author: #{author.text.strip}"
|
|
31
|
+
puts
|
|
32
|
+
|
|
33
|
+
puts "=== Example Complete ===\n"
|
|
34
|
+
puts "Note: This example uses RXerces directly without Nokogiri compatibility."
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
require 'rxerces'
|
|
2
|
+
require 'rxerces/nokogiri'
|
|
3
|
+
|
|
4
|
+
puts "=== RXerces XPath Example ===\n\n"
|
|
5
|
+
|
|
6
|
+
xml = <<-XML
|
|
7
|
+
<bookstore>
|
|
8
|
+
<book category="fiction">
|
|
9
|
+
<title>1984</title>
|
|
10
|
+
<author>George Orwell</author>
|
|
11
|
+
<year>1949</year>
|
|
12
|
+
<price>15.99</price>
|
|
13
|
+
</book>
|
|
14
|
+
<book category="fiction">
|
|
15
|
+
<title>Brave New World</title>
|
|
16
|
+
<author>Aldous Huxley</author>
|
|
17
|
+
<year>1932</year>
|
|
18
|
+
<price>14.99</price>
|
|
19
|
+
</book>
|
|
20
|
+
<book category="non-fiction">
|
|
21
|
+
<title>Sapiens</title>
|
|
22
|
+
<author>Yuval Noah Harari</author>
|
|
23
|
+
<year>2011</year>
|
|
24
|
+
<price>18.99</price>
|
|
25
|
+
</book>
|
|
26
|
+
</bookstore>
|
|
27
|
+
XML
|
|
28
|
+
|
|
29
|
+
doc = RXerces.XML(xml)
|
|
30
|
+
|
|
31
|
+
puts "1. Finding all books:"
|
|
32
|
+
books = doc.xpath('//book')
|
|
33
|
+
puts " Found #{books.length} books"
|
|
34
|
+
puts
|
|
35
|
+
|
|
36
|
+
puts "2. Finding all titles:"
|
|
37
|
+
titles = doc.xpath('//title')
|
|
38
|
+
titles.each do |title|
|
|
39
|
+
puts " - #{title.text.strip}"
|
|
40
|
+
end
|
|
41
|
+
puts
|
|
42
|
+
|
|
43
|
+
puts "3. Finding all authors:"
|
|
44
|
+
authors = doc.xpath('//author')
|
|
45
|
+
authors.each do |author|
|
|
46
|
+
puts " - #{author.text.strip}"
|
|
47
|
+
end
|
|
48
|
+
puts
|
|
49
|
+
|
|
50
|
+
puts "4. Using absolute paths:"
|
|
51
|
+
bookstore_books = doc.xpath('/bookstore/book')
|
|
52
|
+
puts " Found #{bookstore_books.length} books via absolute path"
|
|
53
|
+
puts
|
|
54
|
+
|
|
55
|
+
puts "5. Querying from a specific node:"
|
|
56
|
+
first_book = books[0]
|
|
57
|
+
title = first_book.xpath('.//title').first
|
|
58
|
+
author = first_book.xpath('.//author').first
|
|
59
|
+
puts " First book: #{title.text.strip} by #{author.text.strip}"
|
|
60
|
+
puts
|
|
61
|
+
|
|
62
|
+
puts "6. Combining XPath with Ruby methods:"
|
|
63
|
+
puts " All books with their details:"
|
|
64
|
+
books.each_with_index do |book, i|
|
|
65
|
+
title_node = book.xpath('.//title').first
|
|
66
|
+
author_node = book.xpath('.//author').first
|
|
67
|
+
year_node = book.xpath('.//year').first
|
|
68
|
+
price_node = book.xpath('.//price').first
|
|
69
|
+
|
|
70
|
+
puts " Book #{i + 1}:"
|
|
71
|
+
puts " Title: #{title_node.text.strip}"
|
|
72
|
+
puts " Author: #{author_node.text.strip}"
|
|
73
|
+
puts " Year: #{year_node.text.strip}"
|
|
74
|
+
puts " Price: $#{price_node.text.strip}"
|
|
75
|
+
puts " Category: #{book['category']}"
|
|
76
|
+
puts
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
puts "7. Filtering with Ruby:"
|
|
80
|
+
puts " Fiction books only:"
|
|
81
|
+
fiction_books = books.select { |book| book['category'] == 'fiction' }
|
|
82
|
+
fiction_books.each do |book|
|
|
83
|
+
title = book.xpath('.//title').first
|
|
84
|
+
puts " - #{title.text.strip}"
|
|
85
|
+
end
|
|
86
|
+
puts
|
|
87
|
+
|
|
88
|
+
puts "8. Finding nested elements:"
|
|
89
|
+
all_prices = doc.xpath('//book/price')
|
|
90
|
+
puts " Found #{all_prices.length} prices:"
|
|
91
|
+
total = 0
|
|
92
|
+
all_prices.each do |price|
|
|
93
|
+
amount = price.text.strip.to_f
|
|
94
|
+
total += amount
|
|
95
|
+
puts " - $#{amount}"
|
|
96
|
+
end
|
|
97
|
+
puts " Total: $#{'%.2f' % total}"
|
|
98
|
+
puts
|
|
99
|
+
|
|
100
|
+
puts "9. Nokogiri compatibility:"
|
|
101
|
+
nokogiri_doc = Nokogiri.XML(xml)
|
|
102
|
+
nokogiri_books = nokogiri_doc.xpath('//book')
|
|
103
|
+
puts " Parsed with Nokogiri: #{nokogiri_books.length} books found"
|
|
104
|
+
puts
|
|
105
|
+
|
|
106
|
+
puts "=== Example Complete ==="
|
|
107
|
+
puts "\nNote: Xerces-C supports the XML Schema XPath subset."
|
|
108
|
+
puts "For advanced filtering, combine basic XPath with Ruby methods."
|