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