基于子元素值的 JAXB Unmarshal 子类
JAXB Unmarshal subclass based in child element value
我需要根据元素类型的值来映射 xml。
BaseEntity superclass 与 Machine 和 Robot classes 具有共同的元素。每个 subclasse 都有不同的元素...
XML 结构是固定的,我不能改变它。
每个入口元素应该映射到相应的 class,entry/type=Machine 应该映射到 subclass 机器等等...
在 JAXB 中可能吗?我该如何实施?
有什么建议吗?
<root>
<entries>
<entry>
<name>RTM</name>
<description>RealTime Machine</description>
<code>RTM1</code>
<type>Machine</type>
</entry>
<entry>
<name>RTM</name>
<description>RealTime Machine</description>
<type>Robot</type>
<serial>RS123<serial>
</entry>
</entries>
</root>
public abstract class BaseEntity {
private String name;
private String description;
}
public class Machine extends BaseEntity{
private String code;
}
public class Robot extends BaseEntity{
private String serial;
}
不幸的是,您的 XML 布局非常糟糕。当 JAXB 解析器进入一个入口节点时,它无法通过该节点判断它是否是机器订单机器人。所以它必须猜测使用哪种类型进行解组。通常您会使用类型作为节点名称 (1) 或提供带有类型参数的属性 (2).
1:
<entries>
<machine>...</machine>
<robot>...</robot>
</entries>
2:
<entries>
<entry type="machine">...</entry>
<entry type="robot">...</entry>
</entries>
所以我对你问题的解决方案和你的问题一样科学怪人 XML。
有一个改编的 class,它扩展了基础 class 并具有可用子 class 的所有属性:
@XmlRootElement(name = "ENTRY")
@XmlAccessorType(XmlAccessType.FIELD)
public class AdaptedBaseEntry extends BaseEntry
{
@XmlElement(name = "CODE", required = false)
public String code;
@XmlElement(name = "SERIAL", required = false)
public String serial;
public AdaptedBaseEntry()
{
super();
}
public AdaptedBaseEntry(BaseEntry entry)
{
super(entry.type, entry.description, entry.name);
if (entry instanceof Machine)
{
this.code = ((Machine) entry).code;
} else if (entry instanceof Robot)
{
this.serial = ((Robot) entry).serial;
}
}
}
使用 XmlAdapter
将适配器 class 绑定到 XML,反之亦然:
public class BaseEntryAdapter extends XmlAdapter<AdaptedBaseEntry, BaseEntry>
{
@Override
public BaseEntry unmarshal(AdaptedBaseEntry v) throws Exception
{
switch (v.type)
{
case Machine:
return new Machine(v.name, v.description, v.code);
case Robot:
return new Robot(v.name, v.description, v.serial);
default:
return null;
}
}
@Override
public AdaptedBaseEntry marshal(BaseEntry v) throws Exception
{
return new AdaptedBaseEntry(v);
}
}
我需要根据元素类型的值来映射 xml。 BaseEntity superclass 与 Machine 和 Robot classes 具有共同的元素。每个 subclasse 都有不同的元素... XML 结构是固定的,我不能改变它。 每个入口元素应该映射到相应的 class,entry/type=Machine 应该映射到 subclass 机器等等...
在 JAXB 中可能吗?我该如何实施? 有什么建议吗?
<root>
<entries>
<entry>
<name>RTM</name>
<description>RealTime Machine</description>
<code>RTM1</code>
<type>Machine</type>
</entry>
<entry>
<name>RTM</name>
<description>RealTime Machine</description>
<type>Robot</type>
<serial>RS123<serial>
</entry>
</entries>
</root>
public abstract class BaseEntity {
private String name;
private String description;
}
public class Machine extends BaseEntity{
private String code;
}
public class Robot extends BaseEntity{
private String serial;
}
不幸的是,您的 XML 布局非常糟糕。当 JAXB 解析器进入一个入口节点时,它无法通过该节点判断它是否是机器订单机器人。所以它必须猜测使用哪种类型进行解组。通常您会使用类型作为节点名称 (1) 或提供带有类型参数的属性 (2).
1:
<entries>
<machine>...</machine>
<robot>...</robot>
</entries>
2:
<entries>
<entry type="machine">...</entry>
<entry type="robot">...</entry>
</entries>
所以我对你问题的解决方案和你的问题一样科学怪人 XML。
有一个改编的 class,它扩展了基础 class 并具有可用子 class 的所有属性:
@XmlRootElement(name = "ENTRY")
@XmlAccessorType(XmlAccessType.FIELD)
public class AdaptedBaseEntry extends BaseEntry
{
@XmlElement(name = "CODE", required = false)
public String code;
@XmlElement(name = "SERIAL", required = false)
public String serial;
public AdaptedBaseEntry()
{
super();
}
public AdaptedBaseEntry(BaseEntry entry)
{
super(entry.type, entry.description, entry.name);
if (entry instanceof Machine)
{
this.code = ((Machine) entry).code;
} else if (entry instanceof Robot)
{
this.serial = ((Robot) entry).serial;
}
}
}
使用 XmlAdapter
将适配器 class 绑定到 XML,反之亦然:
public class BaseEntryAdapter extends XmlAdapter<AdaptedBaseEntry, BaseEntry>
{
@Override
public BaseEntry unmarshal(AdaptedBaseEntry v) throws Exception
{
switch (v.type)
{
case Machine:
return new Machine(v.name, v.description, v.code);
case Robot:
return new Robot(v.name, v.description, v.serial);
default:
return null;
}
}
@Override
public AdaptedBaseEntry marshal(BaseEntry v) throws Exception
{
return new AdaptedBaseEntry(v);
}
}