Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

XML Namespace improvements #11072

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
227 changes: 190 additions & 37 deletions spec/std/xml/xml_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -168,45 +168,198 @@ describe XML do
errors[0].to_s.should eq("Opening and ending tag mismatch: people line 1 and foo")
end

it "gets root namespaces scopes" do
doc = XML.parse(<<-XML
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/">
</feed>
XML
)
namespaces = doc.root.not_nil!.namespace_scopes
describe "#namespace" do
describe "when the node has a namespace" do
describe "with a prefix" do
it "return the prefixed namespace" do
doc = XML.parse(<<-XML)
<?xml version="1.0" encoding="UTF-8"?>
<openSearch:feed xmlns:foo="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/"></feed>
XML

namespace = doc.root.not_nil!.namespace.should be_a XML::Namespace
namespace.href.should eq "http://a9.com/-/spec/opensearchrss/1.0/"
namespace.prefix.should eq "openSearch"
end
end

describe "with a default prefix" do
it "return the default namespace" do
doc = XML.parse(<<-XML)
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns:foo="http://www.w3.org/2005/Atom" xmlns="http://a9.com/-/spec/opensearchrss/1.0/"></feed>
XML

namespace = doc.root.not_nil!.namespace.should be_a XML::Namespace
namespace.href.should eq "http://a9.com/-/spec/opensearchrss/1.0/"
namespace.prefix.should be_nil
end
end

describe "without an explicit declaration on the node" do
it "returns the related namespace" do
doc = XML.parse(<<-XML)
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:a="https://a-namespace">
<name></name>
<a:age></a:age>
</feed>
XML

root = doc.root.not_nil!

namespace = root.children[1].namespace.should be_a XML::Namespace
namespace.href.should eq "http://www.w3.org/2005/Atom"
namespace.prefix.should be_nil

namespace = root.children[3].namespace.should be_a XML::Namespace
namespace.href.should eq "https://a-namespace"
namespace.prefix.should eq "a"
end
end
end

describe "when the node does not have namespace" do
it "should return nil" do
doc = XML.parse(<<-XML)
<?xml version="1.0" encoding="UTF-8"?>
<feed></feed>
XML

doc.root.not_nil!.namespace.should be_nil
end
end

namespaces.size.should eq(2)
namespaces[0].href.should eq("http://www.w3.org/2005/Atom")
namespaces[0].prefix.should be_nil
namespaces[1].href.should eq("http://a9.com/-/spec/opensearchrss/1.0/")
namespaces[1].prefix.should eq("openSearch")
describe "when the element does not have a namespace, but has namespace declarations" do
it "should return nil" do
doc = XML.parse(<<-XML)
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns:foo="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/"></feed>
XML

doc.root.not_nil!.namespace.should be_nil
end
end
end

it "returns empty array if no namespaces scopes exists" do
doc = XML.parse(<<-XML
<?xml version='1.0' encoding='UTF-8'?>
<name>John</name>
XML
)
namespaces = doc.root.not_nil!.namespace_scopes
describe "#namespace_definitions" do
it "returns namespaces explicitly defined" do
doc = XML.parse(<<-XML)
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/">
<item xmlns:c="http://c"></item>
</feed>
XML

namespaces.size.should eq(0)
namespaces = doc.root.not_nil!.first_element_child.not_nil!.namespace_definitions

namespaces.size.should eq(1)
namespaces[0].href.should eq("http://c")
namespaces[0].prefix.should eq "c"
end

it "returns an empty array if no namespaces are defined" do
doc = XML.parse(<<-XML)
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/">
<item></item>
</feed>
XML

doc.root.not_nil!.first_element_child.not_nil!.namespace_definitions.should be_empty
end
end

it "gets root namespaces as hash" do
doc = XML.parse(<<-XML
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/">
</feed>
XML
)
namespaces = doc.root.not_nil!.namespaces
namespaces.should eq({
"xmlns" => "http://www.w3.org/2005/Atom",
"xmlns:openSearch": "http://a9.com/-/spec/opensearchrss/1.0/",
})
describe "#namespace_scopes" do
it "gets root namespaces scopes" do
doc = XML.parse(<<-XML)
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/">
</feed>
XML

namespaces = doc.root.not_nil!.namespace_scopes

namespaces.size.should eq(2)
namespaces[0].href.should eq("http://www.w3.org/2005/Atom")
namespaces[0].prefix.should be_nil
namespaces[1].href.should eq("http://a9.com/-/spec/opensearchrss/1.0/")
namespaces[1].prefix.should eq("openSearch")
end

it "returns empty array if no namespaces scopes exists" do
doc = XML.parse(<<-XML)
<?xml version='1.0' encoding='UTF-8'?>
<name>John</name>
XML

namespaces = doc.root.not_nil!.namespace_scopes

namespaces.size.should eq(0)
end

it "includes parent namespaces" do
doc = XML.parse(<<-XML)
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/">
<item xmlns:c="http://c"></item>
</feed>
XML

namespaces = doc.root.not_nil!.first_element_child.not_nil!.namespace_scopes

namespaces.size.should eq(3)
namespaces[0].href.should eq("http://c")
namespaces[0].prefix.should eq "c"
namespaces[1].href.should eq("http://www.w3.org/2005/Atom")
namespaces[1].prefix.should be_nil
namespaces[2].href.should eq("http://a9.com/-/spec/opensearchrss/1.0/")
namespaces[2].prefix.should eq("openSearch")
end
end

describe "#namespaces" do
it "gets root namespaces as hash" do
doc = XML.parse(<<-XML)
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/">
</feed>
XML

namespaces = doc.root.not_nil!.namespaces
namespaces.should eq({
"xmlns" => "http://www.w3.org/2005/Atom",
"xmlns:openSearch" => "http://a9.com/-/spec/opensearchrss/1.0/",
})
end

it "includes parent namespaces" do
doc = XML.parse(<<-XML)
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/">
<item xmlns:c="http://c"></item>
</feed>
XML

namespaces = doc.root.not_nil!.first_element_child.not_nil!.namespaces
namespaces.should eq({
"xmlns:c" => "http://c",
"xmlns" => "http://www.w3.org/2005/Atom",
"xmlns:openSearch" => "http://a9.com/-/spec/opensearchrss/1.0/",
})
end

it "returns an empty hash if there are no namespaces" do
doc = XML.parse(<<-XML)
<?xml version="1.0" encoding="UTF-8"?>
<feed>
<item></item>
</feed>
XML

namespaces = doc.root.not_nil!.first_element_child.not_nil!.namespaces
namespaces.should eq({} of String => String?)
end
end

it "reads big xml file (#1455)" do
Expand All @@ -217,11 +370,11 @@ describe XML do
end

it "sets node text/content" do
doc = XML.parse(<<-XML
doc = XML.parse(<<-XML)
<?xml version='1.0' encoding='UTF-8'?>
<name>John</name>
XML
)

root = doc.root.not_nil!
root.text = "Peter"
root.text.should eq("Peter")
Expand All @@ -231,11 +384,11 @@ describe XML do
end

it "doesn't set invalid node content" do
doc = XML.parse(<<-XML
doc = XML.parse(<<-XML)
<?xml version='1.0' encoding='UTF-8'?>
<name>John</name>
XML
)

root = doc.root.not_nil!
expect_raises(Exception, "Cannot escape") do
root.content = "\0"
Expand Down
22 changes: 15 additions & 7 deletions src/xml/node.cr
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,19 @@ class XML::Node
end
end

# Returns namespaces defined on self element directly.
def namespace_definitions : Array(Namespace)
namespaces = [] of Namespace

ns = @node.value.ns_def
while ns
namespaces << Namespace.new(document, ns)
ns = ns.value.next
end

namespaces
end

# Returns namespaces in scope for self – those defined on self element
# directly or any ancestor node – as an `Array` of `XML::Namespace` objects.
#
Expand All @@ -304,13 +317,8 @@ class XML::Node
def namespace_scopes : Array(Namespace)
scopes = [] of Namespace

ns_list = LibXML.xmlGetNsList(@node.value.doc, @node)

if ns_list
while ns_list.value
scopes << Namespace.new(document, ns_list.value)
ns_list += 1
end
each_namespace do |namespace|
scopes << namespace
end

scopes
Expand Down