如何使用JAXB根据XML中的条件解析为不同的类?

How to parse to different classes based on condition in XML using JAXB?

提问人:worm359 提问时间:1/30/2023 最后编辑:worm359 更新时间:1/30/2023 访问量:200

问:

我有一个使用 JAXB 进行 XML 解析的 Java 应用程序。我需要根据另一个标签的值将其中一个子标签解析为不同的 JAXB 类(想想“version”标签,导致 XML 格式略有不同)。
这是我正在解析的 JAXB 类的代码:
Request

@XmlRootElement(name = "request")
@XmlAccessorType(XmlAccessType.FIELD)
public class Request {
    @XmlElement(name = "fields1")
    private List<Field1> fields1;
    @XmlElement(name = "problemTag")
    @XmlJavaTypeAdapter(value = JavaBeanAdapter.class)
    private List<BaseClass> problemTag;
    ...

以下是我目前所掌握的一些细节:

  • 在我的代码中,我有一个接口。它的实现包含与 JAXB 相关的逻辑(编组和解组)。IXmlProcessor
  • 我为我的问题标签写了一个。它增加了使用具有不同 JAXB 上下文的两个附加实例的额外复杂性。XmlAdapterIXmlProcessor
  • 我宣布这是春豆。XmlAdapter
  • 我在创建 .XmlAdapterjavax.xml.bind.Unmarshaller
  • BaseClass是 XML 的“旧”版本的 JAXB 类,并带有较新版本的附加字段; 当然扩展ExtendingClassExtendingClassBaseClass

因此,我的测试实现中的流程是这样的:

  1. IXmlProcessor#read(String xml, Class<Z> clazz)
  2. 在我的习俗中将被调用。javax.xml.bind.Unmarshaller#unmarshalXmlAdapter
  3. 反过来,将调用它自己的 IXmlProcessor 实例来解析为 或 .XmlAdapterStringBaseClassExtendingClass

下面是代码示例:

public class JavaBeanAdapter extends XmlAdapter<Object, BaseClass> {
    private IXmlProcessor xmlOld;
    private IXmlProcessor xmlNew;
    
    //IXmlProcessor, holding JAXBContext with BaseClass
    public void setXmlOld(IXmlProcessor xml) {
        this.xmlOld = xml;
    }
    //IXmlProcessor, holding JAXBContext with ExtendingClass
    public void setXmlNew(IXmlProcessor xml) {
        this.xmlNew = xml;
    }

    //First, I parse org.w3c.dom.Element back to string.
    //Than I can inspect raw XML.
    //This is just test solution, obviously too sloppy to be used in prod, just to get an insight to what i want to achieve
    @Override
    public BaseClass unmarshal(Object v) {
        if (v == null) return null;
        String rawXmlTag = xmlNew.asStringNonRoot(v, Object.class, ((Element) v).getTagName());
        BaseClass req;
        if (rawXmlTag.contains("someSPecificTagForNewVersionOfXml")) {
            req = xmlNew.read(rawXmlTag, ExtendingClass.class);
        } else {
            req = xmlOld.read(rawXmlTag, BaseClass.class);
        }
        return req;
    }

    @Override
    public Object marshal(BaseClass v) throws Exception {
        if (v == null) return null;
        else if (v instanceof ExtendingClass) return xmlNew.asStringNonRoot((ExtendingClass) v,
                ExtendingClass.class, "BaseClass");
        else return xmlOld.asStringNonRoot(v, BaseClass.class, "BaseClass");
    }
}

然后,我手动将此适配器添加到主实现中的解组器中。 当然,我不满意.
部分实现代码(用于理解那里的内容):
IXmlProcessorrawXmlTag.contains(...)IXmlProcessor

    public <Z> Z read(String xml, Class<Z> clazz) {
        try {
            XMLStreamReader xmlStreamReader = xmlInputFactory.createXMLStreamReader(new StringReader(xml));
            return genUnmarshaller(clazz).unmarshal(xmlStreamReader, clazz).getValue();
        } catch (Exception e) {
            log.error(ExceptionUtils.getStackTrace(e));
            throw errorGenerator.throwableRuntime(CommonScoringError.BAD_XML);
        }

    }
@Override
    public <Z> String asStringNonRoot(Z xmlObj, Class<Z> clazz, String rootName) {
        try {
            StringWriter strWriter = new StringWriter();
            Marshaller marshaller = genMarshaller(xmlObj);

            QName qName = new QName(null, rootName);
            JAXBElement<Z> root = new JAXBElement<Z>(qName, clazz, xmlObj);
            marshaller.marshal(root, strWriter);
            return strWriter.toString();
        } catch (Throwable e) {
            log.error("Error serializing object to XML string: {}", ExceptionUtils.getStackTrace(e));
            throw new IllegalStateException("cannot serialize object to XML");
        }
    }
@Override
    public <Z> Z read(org.w3c.dom.Document xml, Class<Z> clazz) {
        try {
            return genUnmarshaller(clazz).unmarshal(xml, clazz).getValue();
        } catch (Exception e) {
            log.error(ExceptionUtils.getStackTrace(e));
            throw errorGenerator.throwableRuntime(CommonScoringError.BAD_XML);
        }
    }

我的问题如下:

  • 是否有任何替代解决方案可以在不添加具有不同 JAXB 上下文的其他实例的情况下解决此问题?如何改进和简化此实现?IXmlProcessor
  • 也许我应该在解组后检查rootObject.version值,然后手动设置/的正确版本。我应该如何精确地获取原始 Xml?BaseClassExtendingClassBaseClassExtendingClass

任何帮助将不胜感激。

我使用 XmlAdapter 手动找出 xml 格式。它解决了问题,但会导致难以理解、可疑的代码。

Java Spring XML 解析 JAXB 解组

评论


答: 暂无答案