Java 使用JAXB将具有映射式结构的xml解组到对象

Java 使用JAXB将具有映射式结构的xml解组到对象,java,xml,jaxb,Java,Xml,Jaxb,我正在尝试将设计糟糕的XML解组到一个对象中。XML是使用一个通用的类型元素构建的,该元素可以有任意数量的项和任何名称。根据下面Something中type的值,include属性将有所不同。它基本上只是绕过了XSD-rulse(是的,它有一个XSD,但它完全没有用) 我得到的XML: <Something type="actualType"> <Property name="prop1">value1</Property> <Propert

我正在尝试将设计糟糕的XML解组到一个对象中。XML是使用一个通用的
类型
元素构建的,该元素可以有任意数量的
和任何
名称
。根据下面
Something
type
的值,include属性将有所不同。它基本上只是绕过了XSD-rulse(是的,它有一个XSD,但它完全没有用)

我得到的XML:

<Something type="actualType">
   <Property name="prop1">value1</Property>
   <Property name="prop2">value2</Property>
   ...
</Something>
实际问题:


Jaxb中是否有类似的基本支持(没有外部依赖)?如果没有,我是否可以编写自定义注释,以便能够将相同的逻辑用于使用此模式的其他服务

有几种方法可以支持此用例:

选项#1-任何JAXB(JSR-222)实现

如果只需要将XML读入对象,那么可以利用
StreamReaderDelegate
执行以下操作。基本上,它使坏XML看起来就像是好XML:

import javax.xml.bind.*;
import javax.xml.stream.*;
import javax.xml.stream.util.StreamReaderDelegate;
import javax.xml.transform.stream.StreamSource;

public class Demo {

    public static void main(String[] args) throws Exception {

        XMLInputFactory xif = XMLInputFactory.newFactory();
        XMLStreamReader xsr = xif.createXMLStreamReader(new StreamSource("src/forum16529016/input.xml"));

        xsr = new StreamReaderDelegate(xsr) {

            @Override
            public String getLocalName() {
                String localName = super.getLocalName();
                if(!"Property".equals(localName) && super.getEventType() == XMLStreamReader.START_ELEMENT) {
                    return localName;
                }
                if(super.getEventType() == XMLStreamReader.START_ELEMENT) {
                    return super.getAttributeValue(null, "name");
                }
                return localName;
            }
        };

        JAXBContext jc = JAXBContext.newInstance(ActualType.class);
        Unmarshaller unmarshaller = jc.createUnmarshaller();
        ActualType actualType = unmarshaller.unmarshal(xsr, ActualType.class).getValue();
        System.out.println(actualType.prop1);
        System.out.println(actualType.prop2);
    }
}
选项2-EclipseLink JAXB(MOXy的)@XmlPath扩展

注意:我是专家组的负责人和成员

MOXy有一个
@XmlPath
扩展,使您能够映射此用例

import javax.xml.bind.annotation.XmlType;
import org.eclipse.persistence.oxm.annotations.XmlPath;

@XmlType(name="actualType")
public class ActualType
{
   @XmlPath("Property[@name='prop1']/text())
   public X prop1

   @XmlPath("Property[@name='prop1']/text())
   public Y prop2

}
了解更多信息


这不是对您问题的直接回答,但您可以简单地读取错误的xml,因为它们位于对象bad中,然后使用构造函数Good(bad bad bad)定义类Good。好的类将把属性定义为成员,它的构造函数将从坏的对象中获取它们


另一种方法是只使用一个类,但将“坏”成员保持为私有,然后在解组后调用一些方法来填充好成员(jaxb未触及)。请参阅。

很遗憾,我希望避免使用外部库。@LewisDiamond-根据您的环境,MOXy可能不是外部依赖项。MOXy是WebLogic 12.1.1(及更高版本)中默认的JAXB impl,而MOXy包含在GlassFish 3.1.2(及更高版本)中。@LewisDiamond-您只需要担心解组吗?如果您的意思是解组为输入等错误格式,则不需要。我只会解组为正确的xml。请注意,GlassFish或WebLogic不可用。@LewisDiamond-我已经用一个示例更新了我的答案,说明了如何使用
StreamReaderDelegate
使坏的XML看起来像好的XML。由于
StreamReaderDelegate
XMLStreamReader
,因此可以让JAXB解组它。
import javax.xml.bind.*;
import javax.xml.stream.*;
import javax.xml.stream.util.StreamReaderDelegate;
import javax.xml.transform.stream.StreamSource;

public class Demo {

    public static void main(String[] args) throws Exception {

        XMLInputFactory xif = XMLInputFactory.newFactory();
        XMLStreamReader xsr = xif.createXMLStreamReader(new StreamSource("src/forum16529016/input.xml"));

        xsr = new StreamReaderDelegate(xsr) {

            @Override
            public String getLocalName() {
                String localName = super.getLocalName();
                if(!"Property".equals(localName) && super.getEventType() == XMLStreamReader.START_ELEMENT) {
                    return localName;
                }
                if(super.getEventType() == XMLStreamReader.START_ELEMENT) {
                    return super.getAttributeValue(null, "name");
                }
                return localName;
            }
        };

        JAXBContext jc = JAXBContext.newInstance(ActualType.class);
        Unmarshaller unmarshaller = jc.createUnmarshaller();
        ActualType actualType = unmarshaller.unmarshal(xsr, ActualType.class).getValue();
        System.out.println(actualType.prop1);
        System.out.println(actualType.prop2);
    }
}
import javax.xml.bind.annotation.XmlType;
import org.eclipse.persistence.oxm.annotations.XmlPath;

@XmlType(name="actualType")
public class ActualType
{
   @XmlPath("Property[@name='prop1']/text())
   public X prop1

   @XmlPath("Property[@name='prop1']/text())
   public Y prop2

}