Java JAXB:根据元素值解组到子类

Java JAXB:根据元素值解组到子类,java,xml,jaxb,Java,Xml,Jaxb,我正在尝试使用JAXB解析一个XML文件,该文件包含一个项目列表。项目的类别取决于XML中元素的值 这是一个遗留系统,我无法轻松更改输入格式 例如,给定以下XML和类定义: <root> <type>a</type> <item> <a>a1</a> </item> <item> <a>a2</a> </

我正在尝试使用JAXB解析一个XML文件,该文件包含一个项目列表。项目的类别取决于XML中元素的值

这是一个遗留系统,我无法轻松更改输入格式

例如,给定以下XML和类定义:

<root>
    <type>a</type>
    <item>
       <a>a1</a>
    </item>
    <item>
       <a>a2</a>
    </item>
</root>

@XmlRootElement(name = "root")
public class Root {
    @XmlElement
    String type;

    @XmlElement(name="item")
    List<Item> items;
}

public class Item {}
public class ItemA extends Item {
    @XmlElement
    String a;
}
public class ItemB extends Item {
    @XmlElement
    String b;
}

A.
a1
a2
@XmlRootElement(name=“root”)
公共类根{
@XmlElement
字符串类型;
@xmlement(name=“item”)
清单项目;
}
公共类项{}
公共类ItemA扩展了Item{
@XmlElement
字符串a;
}
公共类ItemB扩展了Item{
@XmlElement
b串;
}
现在,items列表包含两个Item对象

我需要结果根对象中的items列表包含两个ItemA对象,一个带有a=“a1”,另一个带有a=“a2”

如果type元素是“b”,我需要items列表包含ItemB对象

在单个XML文件中只指定一个类型元素


我见过几个使用属性值的解决方案,但没有一个使用元素值。

我提出了一个使用@xmlanyement和Blaise Doughan描述的技术的解决方案

根类变为:

@XmlRootElement(name = "root")
public class Root {
    @XmlElement
    String type;

    @XmlAnyElement
    List<Element> other;

    List<Item> items = new ArrayList<Item>();
}
按照提示,您可以创建一个XmlAdapter。不幸的是,适配器在根级别上不可用,因此您必须在反编组/编组期间添加一些额外的代码

根/Item/ItemA/ItemB是纯POJO,此处没有任何注释

适配类型的适配器:

public class RootAdapter extends XmlAdapter<AdaptedRoot, Root>
{
    @Override
    public Root unmarshal( AdaptedRoot v ) throws Exception
    {
        Root root = new Root();
        root.type = v.type;
        for ( AdaptedItem adaptedItem : v.items )
        {
            if ( v.type.equals( "a" ) )
            {
                ItemA a = new ItemA();
                a.a = adaptedItem.a;
                root.items.add( a );
            }
            if ( v.type.equals( "b" ) )
            {
                ItemB b = new ItemB();
                b.b = adaptedItem.b;
                root.items.add( b );
            }
        }
        return root;
    }

    @Override
    public AdaptedRoot marshal( Root v ) throws Exception
    {
        AdaptedRoot adapted = new AdaptedRoot();
        adapted.type = v.type;
        for ( Item item : v.items )
        {
            AdaptedItem adaptedItem = new AdaptedItem();
            if ( v.type.equals( "a" ) )
            {
                adaptedItem.a = ((ItemA) item).a;
            }
            if ( v.type.equals( "b" ) )
            {
                adaptedItem.b = ((ItemB) item).b;
            }
            adapted.items.add( adaptedItem );
        }
        return adapted;
    }

    @XmlRootElement( name = "root" )
    public static class AdaptedRoot
    {
        @XmlElement
        String            type;
        @XmlElement( name = "item" )
        List<AdaptedItem> items = new ArrayList<>();
    }

    public static class AdaptedItem
    {
        @XmlElement
        String a;
        @XmlElement
        String b;
    }
}
公共类RootAdapter扩展了XmlAdapter
{
@凌驾
公用根解组器(AdaptedRoot v)引发异常
{
根=新根();
root.type=v.type;
适用于(AdaptedItem AdaptedItem:v.项目)
{
如果(v.类型等于(“a”))
{
ItemA=新的ItemA();
a、 a=自适应项a;
root.items.add(a);
}
如果(v.类型等于(“b”))
{
ItemB=新的ItemB();
b、 b=adaptedItem.b;
根。项。添加(b);
}
}
返回根;
}
@凌驾
公共AdaptedRoot封送处理程序(根v)引发异常
{
AdaptedRoot adapted=新AdaptedRoot();
适应型=v型;
用于(项目:v.项目)
{
AdaptedItem AdaptedItem=新AdaptedItem();
如果(v.类型等于(“a”))
{
adaptedItem.a=((ItemA)item).a;
}
如果(v.类型等于(“b”))
{
adaptedItem.b=((ItemB)item.b;
}
修改。项目。添加(修改编辑项);
}
回归适应;
}
@XmlRootElement(name=“root”)
公共静态类AdaptedRoot
{
@XmlElement
字符串类型;
@xmlement(name=“item”)
列表项=新建ArrayList();
}
公共静态类AdaptedItem
{
@XmlElement
字符串a;
@XmlElement
b串;
}
}
取消编组/编组可以这样做:

public static void main( String[] args ) throws Exception
{
    String rawRootA = "<root><type>a</type><item><a>a1</a></item><item><a>a2</a></item></root>";
    String rawRootB = "<root><type>b</type><item><b>b1</b></item><item><b>b2</b></item></root>";

    Root rootA = unmarshal( rawRootA );
    for ( Item item : rootA.items )
    {
        System.out.println( item.getClass().getSimpleName() );
    }
    print( rootA );

    Root rootB = unmarshal( rawRootB );
    for ( Item item : rootB.items )
    {
        System.out.println( item.getClass().getSimpleName() );
    }
    print( rootB );
}

public static Root unmarshal( String xml ) throws Exception
{
    JAXBContext context = JAXBContext.newInstance( AdaptedRoot.class );
    Unmarshaller unmarshaller = context.createUnmarshaller();
    XmlAdapter<AdaptedRoot, Root> adapter = new RootAdapter();

    AdaptedRoot adapted = (AdaptedRoot) unmarshaller.unmarshal( new StringReader( xml ) );
    return adapter.unmarshal( adapted );
}

public static void print( Root root ) throws Exception
{
    JAXBContext context = JAXBContext.newInstance( AdaptedRoot.class );
    Marshaller marshaller = context.createMarshaller();
    marshaller.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, true );
    XmlAdapter<AdaptedRoot, Root> adapter = new RootAdapter();

    AdaptedRoot adaptedRoot = adapter.marshal( root );
    marshaller.marshal( adaptedRoot, System.out );
}
publicstaticvoidmain(字符串[]args)引发异常
{
字符串rawrota=“aa1a2”;
字符串rawRootB=“bb1b2”;
根rootA=unmarshal(rawrota);
for(项目:rootA.items)
{
System.out.println(item.getClass().getSimpleName());
}
印刷(rootA);
根rootB=unmarshal(rawrotb);
for(项目:rootB.items)
{
System.out.println(item.getClass().getSimpleName());
}
打印(rootB);
}
公共静态根解组器(字符串xml)引发异常
{
JAXBContext context=JAXBContext.newInstance(AdaptedRoot.class);
Unmarshaller Unmarshaller=context.createUnmarshaller();
XmlAdapter=新根适配器();
AdaptedRoot adapted=(AdaptedRoot)unmarshaller.unmarshal(新StringReader(xml));
返回适配器。解组(适配);
}
公共静态无效打印(根目录)引发异常
{
JAXBContext context=JAXBContext.newInstance(AdaptedRoot.class);
Marshaller=context.createMarshaller();
setProperty(marshaller.JAXB_格式化的_输出,true);
XmlAdapter=新根适配器();
AdaptedRoot AdaptedRoot=adapter.marshal(根);
marshaller.marshall(adaptedRoot,System.out);
}
根据预期产出:

ItemA
ItemA
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
    <type>a</type>
    <item>
        <a>a1</a>
    </item>
    <item>
        <a>a2</a>
    </item>
</root>
ItemB
ItemB
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
    <type>b</type>
    <item>
        <b>b1</b>
    </item>
    <item>
        <b>b2</b>
    </item>
</root>
ItemA
项目a
A.
a1
a2
项目B
项目B
B
b1
b2

以下内容可能会有所帮助:谢谢,布莱斯。棘手的是,对象创建是基于同级元素中的信息,而不是item元素本身。我曾考虑在根类中为type使用线程本地静态信息,但我认为使用您的博客文章中的输入会更好。我将用我的新想法编辑我的问题。嗨,约翰,我不知道开关盒部分会去哪里?它是否应该写在根类的构造函数中?在对根对象进行解组后,开关将转到。在根对象解组后,已填充
其他
列表。该开关确定正确的元素类型,for循环解组每个元素并将其存储在
项目
列表中。
ItemA
ItemA
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
    <type>a</type>
    <item>
        <a>a1</a>
    </item>
    <item>
        <a>a2</a>
    </item>
</root>
ItemB
ItemB
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
    <type>b</type>
    <item>
        <b>b1</b>
    </item>
    <item>
        <b>b2</b>
    </item>
</root>