当前位置: 首页 > 面试题库 >

JAXB-将动态生成的名称空间移动到文档根目录

顾宏朗
2023-03-14
问题内容

我有这个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将a序列化SimpleElement为其org.w3c.dom.Element对应项。

这种方法的唯一问题是,名称空间总是以元素级别结束,而不是文档根目录。

有没有一种方法可以在文档根目录下动态声明名称空间?


问题答案:

我的建议是让JAXB实现根据需要编写名称空间声明。只要元素正确地通过名称空间限定,名称空间声明出现的位置就没有关系。

如果您忽略我的建议,则可以使用以下方法。

原始答案

指定要包含在根元素上的命名空间

您可以使用NamespacePrefixMapper扩展将额外的名称空间声明添加到根元素(请参阅:https
:
//jaxb.java.net/nonav/2.2.11/docs/ch05.html#prefixmapper)。您将需要从您自己的对象模型中派生应在根目录中声明哪些名称空间。

注意:
NamespacePrefixMappercom.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属性用于指定NamespacePrefixMapperMarshaller

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>

更新

明确的答案,谢谢。但是,我需要从SimpleElementAdapter访问NSMapper。你有什么建议?我现在看到的唯一方法是使NSMapper成为可变的单例,以便SimpleElementAdapter可以根据需要添加名称空间。

我忘了你XmlAdapter

Java模型

下面是该模型的一个更复杂的迭代,其中不Foo持有DOM元素的实例,而是持有DOM的实例,并将其实例Bar改编为DOM元素的实例。

oo

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;
    }

}

酒吧适配器

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

  1. 它为我们提供了一个简单的事件,可用于收集名称空间声明。
  2. 它实际上不产生任何东西,因此它是我们可以使用的最轻的元帅目标。

    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>


 类似资料:
  • 问题内容: 我是Linux新手。我正在尝试编写一个shell脚本,该脚本将 根据文件的扩展 名将 文件移动到某些文件夹 ,例如在我的downloads文件夹中,我具有混合文件类型的所有文件。我写了以下脚本 将文件添加到此文件夹后,如何使其自动运行?现在,我必须每次手动运行脚本。 还有一个问题,有什么办法可以合并这两个语句 成为一个陈述?我尝试使用(C编程“或”运算符)和逗号,但它们似乎不起作用。

  • 问题内容: 想知道是否有人在使用新功能通过PHP 5.3对类进行命名空间时是否遇到了此问题。 我正在使用单独的类来生成动态类调用,以在应用程序中定义用户类型。基本上,类定义器采用类型的整数表示形式并解释它们,返回一个包含要用作该用户模型的类名的字符串。 我在全局范围内定义了具有该名称的用户类型的对象模型,但是在Editor名称空间中,该用户的编辑器具有另一个名称相同的对象。由于某些原因,PHP不允

  • 请修改配置文件caches\configs\system.php中 'html_root' => '/html',//生成静态文件路径 为 'html_root' => '',//生成静态文件路径

  • 问题内容: 我正在使用Jersey和JAXB构建简单的RESTful Web服务,我将HashMap从’String’转换为’Integer’: 我需要生成一个如下所示的XML响应: 用JAXB生成动态标签名的最佳方法是什么? 问题答案: 您可以使用-annotated属性并将元素返回为s: 这种方法很丑陋,但比它产生的XML丑陋。

  • 是否有一种使用JAXB从非根元素以编程方式获取名称空间的方法? 我可以在根元素上使用以下内容(其中DetailedReport是根元素对象): 但是,如果我尝试使用来自同一个包的类型,但不是根元素,qname为null。 根据JAXBIntrospector的javadocs: 参数对象是以下情况的JAXB元素: 它是javax.xml.bind.JAXBElement的实例,object的类用@

  • 我想让JAXB列表到xml,其中命名空间只出现在根元素中,而不是在任何其他元素中: 我已经尝试了package level @XmlSchema,但是我一定是做错了什么,因为没有显示任何东西。 这是我的根元素类: 我还有以下包-info.java: