我有这个POJO,它封装了Atom条目的动态,非嵌套元素:
public class SimpleElement {
private Namespace namespace;
private String tagName;
private String value;
private Collection<Attribute> attributes;
/* getters/setters/... */
为了完整起见,
Attribute
public class Attribute {
private String name;
private String value;
private Namespace namespace;
/* getters/setters/... */
和
Namespace
:public class Namespace {
private final String uri;
private final String prefix;
/* getters/setters/... */
SimpleElementAdapter
将SimpleElement
序列化为其对应的org.w3c.dom.Element
。这种方法的唯一问题是,名称空间总是以元素级别结束,而不是文档根目录。
有没有一种方法可以在文档根目录下动态声明名称空间?
最佳答案
我的建议
我的建议是让JAXB实现根据需要编写名称空间声明。只要元素正确地通过名称空间限定,名称空间声明出现的位置就没有关系。
如果您忽略我的建议,则可以使用以下方法。
原始答案
指定要包含在根元素上的命名空间
您可以使用NamespacePrefixMapper
扩展名将额外的名称空间声明添加到根元素(请参阅:https://jaxb.java.net/nonav/2.2.11/docs/ch05.html#prefixmapper)。您将需要从您自己的对象模型中派生应在根目录中声明哪些名称空间。
注意: NamespacePrefixMapper
在com.sun.xml.bind.marshaller
包中。这意味着您将在类路径上需要JAXB参考实现罐(请参阅:https://jaxb.java.net/)。
import com.sun.xml.bind.marshaller.*;
public class MyNamespacePrefixMapper extends NamespacePrefixMapper {
@Override
public String getPreferredPrefix(String arg0, String arg1, boolean arg2) {
return null;
}
@Override
public String[] getPreDeclaredNamespaceUris2() {
return new String[] {"ns1", "http://www.example.com/FOO", "ns2", "http://www.example.com/BAR"};
}
}
在
NamespacePrefixMapper
上指定Marshaller
com.sun.xml.bind.namespacePrefixMapper
属性用于在NamespacePrefixMapper
上指定Marshaller
。marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper", new MyNamespacePrefixMapper());
示范代码
Java模型(Foo)
import javax.xml.bind.annotation.*;
@XmlRootElement
public class Foo {
private Object object;
@XmlAnyElement
public Object getObject() {
return object;
}
public void setObject(Object object) {
this.object = object;
}
}
演示
import javax.xml.bind.*;
import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.w3c.dom.Element;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Foo.class);
Foo foo = new Foo();
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document document = db.newDocument();
Element element = document.createElementNS("http://www.example.com/FOO", "ns1:foo");
foo.setObject(element);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper", new MyNamespacePrefixMapper());
marshaller.marshal(foo, System.out);
}
}
输出
以下是将产生的示例输出:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<foo xmlns:ns1="http://www.example.com/FOO" xmlns:ns2="http://www.example.com/BAR">
<ns1:foo/>
</foo>
更新
明确的答案,谢谢。但是,我需要从以下位置访问NSMapper
SimpleElementAdapter。你有什么建议?我看到正确的唯一方法
现在正在将NSMapper变成易变的单例
如果需要,SimpleElementAdapter可以添加名称空间。
我忘记了你的
XmlAdapter
。Java模型
下面是该模型的一个更复杂的迭代,其中不是
Foo
持有DOM元素的实例,而是持有Bar
的实例,该实例被适配为DOM元素的实例。Foo
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
@XmlRootElement
public class Foo {
private Bar bar;
@XmlAnyElement
@XmlJavaTypeAdapter(BarAdapter.class)
public Bar getBar() {
return bar;
}
public void setBar(Bar bar) {
this.bar = bar;
}
}
条形
public class Bar {
private String value;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
BarAdapter
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.parsers.*;
import org.w3c.dom.*;
public class BarAdapter extends XmlAdapter<Object, Bar>{
@Override
public Object marshal(Bar bar) throws Exception {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document document = db.newDocument();
Element element = document.createElementNS("http://www.example.com/BAR", "ns:bar");
element.setTextContent(bar.getValue());
return element;
}
@Override
public Bar unmarshal(Object arg0) throws Exception {
// TODO Auto-generated method stub
return null;
}
}
抓取命名空间声明
由于您的对象模型不直接保存DOM元素,因此您无法遍历它来获取名称空间声明。相反,我们可以对
ContentHandler
进行调度以收集它们。以下是编组到ContentHandler
的原因:NsContentHandler contentHandler = new NsContentHandler();
marshaller.marshal(foo, contentHandler);
NsContentHandler
ContentHandler
的实现类似于:import java.util.*;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
public class NsContentHandler extends DefaultHandler {
private Map<String, String> namespaces = new TreeMap<String, String>();
@Override
public void startPrefixMapping(String prefix, String uri) throws SAXException {
if(!namespaces.containsKey(prefix)) {
namespaces.put(prefix, uri);
}
}
public Map<String, String> getNamespaces() {
return namespaces;
}
}
指定要包含在根元素上的命名空间
MyNamespacePrefixMapper
的实现稍有变化,以使用从我们的ContentHandler
捕获的namrespaces。import java.util.Map;
import java.util.Map.Entry;
import com.sun.xml.bind.marshaller.*;
public class MyNamespacePrefixMapper extends NamespacePrefixMapper {
private String[] namespaces;
public MyNamespacePrefixMapper(Map<String, String> namespaces) {
this.namespaces = new String[namespaces.size() * 2];
int index = 0;
for(Entry<String, String> entry : namespaces.entrySet()) {
this.namespaces[index++] = entry.getKey();
this.namespaces[index++] = entry.getValue();
}
}
@Override
public String getPreferredPrefix(String arg0, String arg1, boolean arg2) {
return null;
}
@Override
public String[] getPreDeclaredNamespaceUris2() {
return namespaces;
}
}
示范代码
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Foo.class);
Bar bar = new Bar();
bar.setValue("Hello World");
Foo foo = new Foo();
foo.setBar(bar);
Marshaller marshaller = jc.createMarshaller();
// Marshal First Time to Get Namespace Declarations
NsContentHandler contentHandler = new NsContentHandler();
marshaller.marshal(foo, contentHandler);
// Marshal Second Time for Real
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper", new MyNamespacePrefixMapper(contentHandler.getNamespaces()));
marshaller.marshal(foo, System.out);
}
}
输出
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<foo xmlns:ns="http://www.example.com/BAR">
<ns:bar>Hello World</ns:bar>
</foo>