使用类似的包装器解组多个 XML

Unmarshalling multiple XMLs with similar wrapper

提问人:František Slabý 提问时间:11/14/2023 更新时间:11/14/2023 访问量:29

问:

我有多个不同的XML具有相似的包装器。例如:

<?xml version="1.0" encoding="UTF-8"?>
<response id="085845">
  <class name="vera-pd" version="1.25" revision="1">
    <method name="platce-info">
      <status>0</status>
      <error_msg/>
      ... method content ...
    </method>
  </class>
</response>

我想根据内容或属性“name”将它们取消编组到 POJO。这样的事情可能吗?我想避免为每个方法编写不同的 ResponseClass。XML不是我的,它们来自不同的系统,所以我无法使用类型或命名空间。

我尝试使用这样的@XmlElements(类 MethodXX1 和 MethodXX2 覆盖 VeraWSMethod)。它不起作用,因为 unmarshaller 无法确定类并在 @XmlElements中使用最后一个。

@XmlRootElement(name = "class")
public class VeraWSClass {
    private String name;
    private String version;
    private String revision;
    private VeraWSMethod method;

    @XmlAttribute
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @XmlAttribute
    public String getVersion() {
        return version;
    }

    public void setVersion(String version) {
        this.version = version;
    }

    @XmlAttribute
    public String getRevision() {
        return revision;
    }

    public void setRevision(String revision) {
        this.revision = revision;
    }

    @XmlElements({
            @XmlElement(name = "method", type = MethodXX1.class),
            @XmlElement(name = "method", type = MethodXX2.class)
    })
    public VeraWSMethod getMethod() {
        return method;
    }

    public void setMethod(VeraWSMethod method) {
        this.method = method;
    }
}
Java XML JAXB

评论


答:

0赞 Rick O'Sullivan 12/5/2023 #1

是的,可以取消封送包含不同内容的 XML 实例,这些内容由具有不同值的给定属性进行区分。

下面是一个独立的 Maven 演示项目zip,它使用这些 JAXB 功能来提供解决方案:

  • @XmlJavaTypeAdapter(MethodXmlAdapter.class)- 一个 JAXB 注解,用于指定一个类以提供自定义的 unmarshallermarshaller 方法。
  • MethodXmlAdapter.class- 在解组方法元素时从 JAXB 接收 DOM 元素。该实例用于将可区分名称读取为 DOM 属性。编组时,JAXB 在适配器中提供要编组的实例,以返回到 DOM 表示。ElementMethodXXNElement

解组的关键点是 distinguishing 属性可以通过编程方式访问,并且 DOM 元素可以解组为所需的 Java 类型。

Context.mainUnmarshalByName(...)

public static Object mainUnmarshalByName(Element element)
{
    Object value = null;
    String name = element.getAttribute("name");
    switch (name)
    {
        case "platce-info":
            value = mainUnmarshal(element, METHODXX1_CLASS);
            break;
        case "platce-data":
            value = mainUnmarshal(element, METHODXX2_CLASS);
            break;
        default:
            value = element;
            break;
    }
    return value;
}

封送的关键点是,可以使用声明的 QName 将不同的实例封送到一个方法元素,无论是否带有命名空间。MethodXXN

Context.mainWrapAsMethod(...)

@SuppressWarnings({ "rawtypes", "unchecked" })
public static Object mainWrapAsMethod(Object value)
{
    Object wrap = null;
    if ( value != null )
    {
        if ( isJAXBElement(value) )
            value = ((JAXBElement) value).getValue();

        if ( METHODXX1_CLASS.equals(value.getClass()) )
            wrap = new JAXBElement(METHOD_QNAME, METHODXX1_CLASS, value);
        else if ( METHODXX2_CLASS.equals(value.getClass()) )
            wrap = new JAXBElement(METHOD_QNAME, METHODXX2_CLASS, value);
    }
    return wrap;
}

实现

样本项目使用 HiSrc HigherJAXB Maven 插件和 HiSrc BasicJAXB 中的 XJC 插件生成 JAXB 类。XML 架构将类型定义为包含公共属性和元素的子类型。MethodXXNMethodBase

XJC 从 platce.xsd 生成的类

org
└── example
    └── platce
        ├── MethodBase.java
        ├── MethodXX1.java
        ├── MethodXX2.java
        ├── ObjectFactory.java
        ├── Response.java
        └── VeraWSClass.java

platce.xsd 架构定义了用于保存 any 元素的类型;但是,HiSrc HigherJAXB Maven 插件使用 HiSrc BasicJAXB 定制 XJC 插件和 HiSrc HyperJAXB Annox 注释 XJC 插件,使用单独的配置文件 VeraWSClass.any.xml 添加 和 注释。 VeraWSClass@XmlAnyElement@XmlJavaTypeAdapter

执行

对于演示,您可以使用以下命令运行测试和/或应用程序:

mvn -Ptest clean test
mvn -Pexec compile exec:java -Dexec.args="src/test/samples/response01.xml"
mvn -Pexec compile exec:java -Dexec.args="src/test/samples/response02.xml"

免責聲明:我是分叉的 HiSrc BasicJAXB、HiSrc HigherJAXB 和 HiSrc HyperJAXB Annox 项目的维护者。