我的建议
我的建议是让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.*;@XmlRootElementpublic 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>
更新
明确的答案,谢谢。但是,我需要从SimpleElementAdapter访问NSMapper。你有什么建议?我现在看到的唯一方法是使NSMapper成为可变的单例,以便SimpleElementAdapter可以根据需要添加名称空间。
我忘了你
XmlAdapter。
Java模型
下面是该模型的一个更复杂的迭代,其中不
Foo持有DOM元素的实例,而是持有DOM的实例,并将其实例
Bar改编为DOM元素的实例。
oo
import javax.xml.bind.annotation.*;import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;@XmlRootElementpublic 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; }}酒吧适配器
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更改的实现可以使用从我们的捕获的namrespaces
ContentHandler。
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>



