Jaxb:如何解组 xs:任何 XML 字符串部分?

2022-01-19 00:00:00 xml java jaxb

我有一个应用程序使用 Jaxb 进行 XML<->转换,并使用 maven-jaxb2-plugin 自动生成类.

I have an application doing XML<->conversions using Jaxb and automatically generated classes with maven-jaxb2-plugin.

在我的架构深处,我可以输入ANY"xml.

Someplace deep in my schema, I have the possibility to enter "ANY" xml.

更新:这更好地描述了我的架构.一些已知的 XML 包装了一个完全未知的部分(任何"部分).

Update: this better describes my schema. Some known XML wrapping a totally unknown part (the "any" part).

<xs:complexType name="MessageType">
  <xs:sequence>
    <xs:element name="XmlAnyPayload" minOccurs="0">
        <xs:complexType>
            <xs:sequence>
                <xs:any namespace="##any"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
    <xs:element name="OtherElements">
        ....
</xs:sequence>

这(通过 jaxb)映射到这样的内部类.

This maps (by jaxb) to a inner class like this.

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "any"
})
public static class XmlAnyPayload {

    @XmlAnyElement(lax = true)
    protected Object any;

当我解组整个结构时,没问题.Object any"将呈现为 org.apache.xerces.dom.ElementNSImpl.现在,我想手动重新创建 Java 对象,然后转到 XML.如何获取一些随机 XML 并放入 any (org.apache.xerces.dom.ElementNSImpl) 元素以构建 Java 对象?

When I unmarshall the entire structure, it is no problem. The "Object any" will render into a org.apache.xerces.dom.ElementNSImpl. Now, I want to recreate the Java object manually and then go to XML. How do I take some random XML and put into the any (org.apache.xerces.dom.ElementNSImpl) element to be able to build up the Java object?

另外,下一种情况是当我将此元素作为 java 时,我想解组这部分(以便能够提取此元素的 XML 字符串).但这是不可能的.我得到一个关于根元素的例外.但是不能对 ElementNSImpl 进行注解.

Also, the next case is when I have this element as java, I want to unmarshall this very part (to be able to extract the XML string of this element). But this is not possible. I get an exception about root elements. But it is not possible to annotate ElementNSImpl.

unable to marshal type "com.sun.org.apache.xerces.internal.dom.ElementNSImpl" as an element because it is missing an @XmlRootElement annotation

您对如何处理这些问题有什么建议吗?

Do you have any suggestions on how to handle these problems?

推荐答案

@XmlAnyElement(lax = true) 用简单的英语表示类似:

@XmlAnyElement(lax = true) means in plain English something like:

亲爱的 JAXB!如果您有此元素的映射,请解组它成一个Java对象.如果您不知道此元素,请将其保留为DOM 元素.

Dear JAXB! If you have a mapping for this element, please unmarshal it into a Java object. If you don't know this element, just leave it as a DOM element.

这正是您的情况.因此,如果您想要真正解组此 lax any 的内容,请为 JAXB 上下文提供您希望解组的元素的映射.最简单的方法是使用 @XmlRootElement

This is exactly what is happening in your case. So if you want to actually unmarshal the content of this lax any, provide JAXB context with a mapping for the element you wish to unmarshal. The easiest way to do this is to annotate your class with @XmlRootElement

@XmlRootElement(name="foo", namespace="urn:bar")
public class MyClass { ... }

现在,当您创建 JAXB 上下文时,将 MyClass 添加到其中:

Now when you create your JAXB context, add MyClass into it:

JAXBContext context = JAXBContext.newInstance(A.class, B.class, ..., MyClass.class);

在这种情况下,如果 JAXB 在该 xs:any 的位置遇到 {urn:bar}foo 元素,它将知道该元素被映射到MyClass 并将尝试解组 MyClass.

In this case, if JAXB meets the {urn:bar}foo element in the place of that xs:any, it will know that this element is mapped onto MyClass and will try to unmarshal MyClass.

如果您基于包名称创建 JAXB 上下文(您可能会这样做),您仍然可以向其中添加您的类(例如,com.acme.foo.MyClass).最简单的方法是创建一个 com/acme/foo/jaxb.in​​dex 资源:

If you are creating JAXB context based on the package name (you probably do), you can still add you class (say, com.acme.foo.MyClass) to it. The easiest way is to create a com/acme/foo/jaxb.index resource:

com.acme.foo.MyClass

然后将你的包名添加到上下文路径中:

And the add your package name to the context path:

JAXBContext context = JAXBContext.newInstance("org.dar.gee.schema:com.acme.foo");

ObjectFactory 等还有其他方法,但 jaxb.in​​dex 的技巧可能是最简单的方法.

There are other ways with ObjectFactory etc., but the trick with jaxb.index is probably the easiest one.

或者,您可以将 xs:any 的内容保留为 DOM,并在第二次使用另一个 JAXB 上下文(它知道您的MyClass 类).比如:

Alternatively, instead of unmarshalling everything in one run, you can leave the content of xs:any as DOM and unmarshal it into the target object in a second unmarshalling with anothe JAXB context (which know your MyClass class). Something like:

JAXBContext payloadContext = JAXBContext.newInstance(MyClass.class);
payloadContext.createUnmarshaller().unmarshal((Node) myPayload.getAny());

这种方法有时会更好,尤其是当您组合了相对独立的容器/有效负载模式时.视情况而定.

This approach is sometimes better, especially when you have a combination of container/payload schemas which are relatively independent. Depends on the case.

上述所有内容也适用于编组.这一切都是双向的.

All said above applies to marshalling as well. It's all neatly bidirectional.

相关文章