Are XmlParser and XmlSlurper namespace aware by default?

时间:2015-10-29 15:46:55

标签: xml groovy namespaces

When answering this question How to read values from XML Request and write into XML Response using Groovy? related to namespaces (SOAP) with XmlSlurper and XmlParser, I realized that I was actually not able to tell if they are non-namespace-aware by default.

While documentation says so (non-namespace-aware by default):

XmlSlurper

XmlSlurper ()

Creates a non-validating and non-namespace-aware XmlSlurper which does not allow DOCTYPE declarations in documents.

XmlParser

XmlParser ()

Creates a non-validating and non-namespace-aware XmlParser which does not allow DOCTYPE declarations in documents.

Code is exactly the opposite (namespace-aware by default):

XmlSlurper:

public XmlSlurper() throws ParserConfigurationException, SAXException {
    this(false, true);
}

XmlParser:

public XmlParser() throws ParserConfigurationException, SAXException {
    this(false, true);
}

I found also this answer claiming that XMLSlurper is non-namespace aware by default.

2 个答案:

答案 0 :(得分:2)

I think that documentation is wrong and XmlSlurper and XmlParser are namespace-aware by default:

def text = '''<person xmlns:city="http://localhost/">
    <name>jalopaba</name>
    <city:name>Valladolid</city:name>
</person>'''

// XmlSlurper is namespace aware, since new XmlSlurper() --> new XmlSlurper(false, true)
def slurped_person = new XmlSlurper().parseText(text)
assert 'jalopabaValladolid' == slurped_person.name.text() // :name and :name from city
assert 'jalopaba' == slurped_person.':name'.text() // aware of default namespace
assert '' == slurped_person.'city:name'.text() // city namespace not declared

// but you need to make the resulting GPathResult aware of the concrete namespace
slurped_person = new XmlSlurper().parseText(text).declareNamespace([city: 'http://localhost/'])
assert 'jalopabaValladolid' == slurped_person.name.text() // :name and :name from city
assert 'jalopaba' == slurped_person.':name'.text() // aware of default namespace
assert 'Valladolid' == slurped_person.'city:name'.text() // city namespace declared

// If you make XmlSlurper not namespace aware, it uses the whole element string
slurped_person = new XmlSlurper(false, false).parseText(text)
assert 'jalopaba' == slurped_person.name.text() // just element 'name'
assert '' == slurped_person.':name'.text() // not aware of default namespace
assert 'Valladolid' == slurped_person.'city:name'.text() // using the whole element string (it seems to be namespace aware, but it's not)

// And when it is not namespace aware, nothing changes declaring namespaces
slurped_person = new XmlSlurper(false, false).parseText(text).declareNamespace([city: 'http://localhost/'])
assert 'jalopaba' == slurped_person.name.text() // just element 'name'
assert '' == slurped_person.':name'.text() // not aware of default namespace
assert 'Valladolid' == slurped_person.'city:name'.text() // using the whole element string (it seems to be namespace aware, but it's not)

def c = new Namespace('http://localhost/', 'city')
assert c.name instanceof QName // => new QName('http://localhost/', 'name', 'city')

// XmlParser is namespace aware, since new XmlParser() --> new XmlParser(false, true)
def parsed_person = new XmlParser().parseText(text)
assert parsed_person.toString() == 'person[attributes={}; value=[name[attributes={}; value=[jalopaba]], {http://localhost/}name[attributes={}; value=[Valladolid]]]]'
assert 'jalopaba' == parsed_person.name.text() // default namespace
assert 'jalopaba' == parsed_person[new QName('name')].text() // default namespace
assert 'jalopabaValladolid' == parsed_person[new QName('http://localhost/', 'name')].text() // aware of namespace and empty string as prefix
assert 'Valladolid' == parsed_person[new QName('http://localhost/', 'name', 'it_does_not_matter')].text() // aware of namespace, prefix does not matter
assert '' == parsed_person[new QName('http://wrong_namespace/', 'name', 'city')].text() // aware of namespace, wrong URI with good prefix gets nothing
assert 'Valladolid' == parsed_person[c.name].text()
assert 'Valladolid' == parsed_person.'city:name'.text() // using the whole element string

// With XmlParser not namespace aware...
parsed_person = new XmlParser(false, false).parseText(text)
assert parsed_person.toString() == 'person[attributes={xmlns:city=http://localhost/}; value=[name[attributes={}; value=[jalopaba]], city:name[attributes={}; value=[Valladolid]]]]'
assert 'jalopaba' == parsed_person.name.text()
assert 'jalopaba' == parsed_person[new QName('name')].text()
assert 'jalopaba' == parsed_person[new QName('http://localhost/', 'name')].text() // ignores namespace, only uses 'name' string
assert '' == parsed_person[new QName('http://localhost/', 'name', 'wrong_prefix')].text() // not aware of namespace, the only important thing is prefix
assert 'Valladolid' == parsed_person[new QName('http://wrong_namespace/', 'name', 'city')].text() // wrong namespace but not aware, the only important thing is prefix
assert 'Valladolid' == parsed_person[c.name].text()
assert 'Valladolid' == parsed_person.'city:name'.text()

答案 1 :(得分:1)

从Groovy 2.4.6开始,文档错误:

/**
 * Creates a non-validating and non-namespace-aware <code>XmlSlurper</code> which does not allow DOCTYPE declarations in documents.
 *
 * @throws ParserConfigurationException if no parser which satisfies the requested configuration can be created.
 * @throws SAXException for SAX errors.
 */
public XmlSlurper() throws ParserConfigurationException, SAXException {
    this(false, true);
}

它实际上创建了一个名称空间感知解析器(第二个参数为true)。

我还报告了一个错误,其中名称空间前缀未正确处理:https://issues.apache.org/jira/browse/GROOVY-7781。幸运的是,另一位用户发布了PR来修复。有人应该用PR来解决文档问题,这会多次给自己和他人造成混淆。