JAXB 将多态 POJO 编组为 XML
JAXB marshal polymorphic POJO to XML
我正在尝试使用 JAXB 编组 class 文件(带注释)。在 <profile-set>
下,它可以有不同的标签,例如
<organization-information-profile>
<connection-profile>
<user-information-profile>
示例输出 XML 文件如下
一)
<?xml version="1.0"?>
<request version="2.0" principal="111" credentials="xxxxx">
<target name="TestAPI" operation="create">
<parameter>
<organization>
<qualified-name>some-qualified-name</qualified-name>
<profile-set>
<name>TestOrg</name>
<organization-information-profile>
<name>Organization Information</name>
<qualified-name>/Organization Information</qualified-name>
<last-name>Test</last-name>
<address>some-address</address>
<city>my-city</city>
<province></province>
<postal-code>1111</postal-code>
<country>Timbaktu</country>
<phone-number-day>1111</phone-number-day>
<email-address>some@email.com</email-address>
<attribute name="PhoneNumber1">
<value context="organization">23333</value>
</attribute>
<attribute name="ShortName">
<value context="organization">my company</value>
</attribute>
<attribute name="TaxId">
<value context="organization">myorg</value>
</attribute>
</organization-information-profile>
</profile-set>
</organization>
</parameter>
</target>
</request>
b)
<?xml version="1.0"?>
<request version="2.0" principal="11111" credentials="xxxxx">
<target name="TestAPI" operation="update">
<parameter>
<organization>
<qualified-name>some-qualified-name</qualified-name>
<profile-set>
<name>TestOrg</name>
<connection-profile>
<qualified-name>some-qualified-name</qualified-name>
<service>
<name>some service</name>
</service>
<attribute name="att-1">
<value context="organization" segment="some-segment" subscript="524288">fill-the-value</value>
</attribute>
<attribute name="att-2">
<value context="organization" segment="some-segment" subscript="524288">qedqwe</value>
</attribute>
</connection-profile>
</profile-set>
</organization>
</parameter>
</target>
</request>
下面是代码(仅配置文件集)
public static class ProfileSet
{
@XmlElement(name = "name")
public String name;
// innerPayLoad is template to store different profile objects
@XmlJavaTypeAdapter(CustomAdaptor.class)
@XmlElement
public InnerPayLoad innerPayLoad;
public ProfileSet(String name, InnerPayLoad innerPayLoad)
{
this.name = name;
this.innerPayLoad = innerPayLoad;
}
}
和自定义适配器
public class CustomAdaptor extends XmlAdapter<String,InnerPayLoad<?>>
{
@Override
public InnerPayLoad<?> unmarshal(String v) throws Exception
{
return null;
}
@Override
public String marshal(InnerPayLoad<?> v) throws Exception
{
String value = TestCode.convertToXmlNoHeader(v.whichProfile,v.whichProfile.getClass());
// after converting value becomes
// <organization-information-profile>
// <name>Organization Information</name>
// </organization-information-profile>
return value;
}
}
但最终生成的 XML 与 organization-information-profile
的 (a) 不相似
<?xml version='1.0' encoding='UTF-8'?>
<request version="2.0" principle="11111" credentials="xxxxx">
<target name="TestAPI" operation="create">
<parameter>
<organization>
<qualified-name>newOrg</qualified-name>
<profile-set>
<innerPayLoad><organization-information-profile>
<name>Organization Information</name>
</organization-information-profile></innerPayLoad>
<name>testOrg</name>
</profile-set>
</organization>
</parameter>
</target>
</request>
是否可以删除 <innerPayLoad>
标记并仅使用 CustomAdaptor 编组函数插入 return 值?
感谢解决此问题的帮助和提示。
您不需要为 ProfileSet
中的各种配置文件类型编写自定义适配器。
相反,要处理这种混合的 XML 内容,规范的方法是这样的。
在你的 ProfileSet
class 你应该定义一个多态的 Java 属性 profile
可以取 <organization.information-profile>
的内容,
<connection-profile>
或 <user-information-profile>
元素。
(我在这里更喜欢名称 profile
而不是 innerPayload
)。
这些 XML 元素名称和 Java classes 之间的映射完成
通过使用 @XmlElements
注释。
@XmlAccessorType(XmlAccessType.FIELD)
public class ProfileSet {
@XmlElement(name = "name")
private String name;
// template to store different profile objects
@XmlElements({
@XmlElement(name = "organization-information-profile", type = OrganizationInfomationProfile.class),
@XmlElement(name = "connection-profile", type = ConnectionProfile.class),
@XmlElement(name = "user-information-profile", type = UserInformationProfile.class)
})
private Profile profile;
// default constructor used by JAXB unmarshaller
public ProfileSet() {
}
public ProfileSet(String name, Profile profile) {
this.name = name;
this.profile = profile;
}
}
您需要一个抽象的超级class Profile
只包含所有类型配置文件共有的属性:
@XmlAccessorType(XmlAccessType.FIELD)
public abstract class Profile {
@XmlElement
private String name;
@XmlElement(name = "attribute")
private List<Attribute> attributes;
}
你有一个子class OrganizationInformationProfile
代表
<organization-information-profile>
元素
@XmlAccessorType(XmlAccessType.FIELD)
public class OrganizationInfomationProfile extends Profile {
@XmlElement(name = "qualified-name")
private String qualifiedName;
@XmlElement(name = "last-name")
private String lastName;
@XmlElement(name = "address")
private String address;
// ... other properties
}
和另一个子class ConnectionProfile
代表<connection-profile>
元素
@XmlAccessorType(XmlAccessType.FIELD)
public class ConnectionProfile extends Profile {
@XmlElement(name = "service")
private Service service;
}
还有另一个子class UserInformationProfile
代表<user-information-profile>
元素。
通过使用上述方法,您可以解组 XML 示例
并在编组时再次获得相同的输出。
我正在尝试使用 JAXB 编组 class 文件(带注释)。在 <profile-set>
下,它可以有不同的标签,例如
<organization-information-profile>
<connection-profile>
<user-information-profile>
示例输出 XML 文件如下
一)
<?xml version="1.0"?>
<request version="2.0" principal="111" credentials="xxxxx">
<target name="TestAPI" operation="create">
<parameter>
<organization>
<qualified-name>some-qualified-name</qualified-name>
<profile-set>
<name>TestOrg</name>
<organization-information-profile>
<name>Organization Information</name>
<qualified-name>/Organization Information</qualified-name>
<last-name>Test</last-name>
<address>some-address</address>
<city>my-city</city>
<province></province>
<postal-code>1111</postal-code>
<country>Timbaktu</country>
<phone-number-day>1111</phone-number-day>
<email-address>some@email.com</email-address>
<attribute name="PhoneNumber1">
<value context="organization">23333</value>
</attribute>
<attribute name="ShortName">
<value context="organization">my company</value>
</attribute>
<attribute name="TaxId">
<value context="organization">myorg</value>
</attribute>
</organization-information-profile>
</profile-set>
</organization>
</parameter>
</target>
</request>
b)
<?xml version="1.0"?>
<request version="2.0" principal="11111" credentials="xxxxx">
<target name="TestAPI" operation="update">
<parameter>
<organization>
<qualified-name>some-qualified-name</qualified-name>
<profile-set>
<name>TestOrg</name>
<connection-profile>
<qualified-name>some-qualified-name</qualified-name>
<service>
<name>some service</name>
</service>
<attribute name="att-1">
<value context="organization" segment="some-segment" subscript="524288">fill-the-value</value>
</attribute>
<attribute name="att-2">
<value context="organization" segment="some-segment" subscript="524288">qedqwe</value>
</attribute>
</connection-profile>
</profile-set>
</organization>
</parameter>
</target>
</request>
下面是代码(仅配置文件集)
public static class ProfileSet
{
@XmlElement(name = "name")
public String name;
// innerPayLoad is template to store different profile objects
@XmlJavaTypeAdapter(CustomAdaptor.class)
@XmlElement
public InnerPayLoad innerPayLoad;
public ProfileSet(String name, InnerPayLoad innerPayLoad)
{
this.name = name;
this.innerPayLoad = innerPayLoad;
}
}
和自定义适配器
public class CustomAdaptor extends XmlAdapter<String,InnerPayLoad<?>>
{
@Override
public InnerPayLoad<?> unmarshal(String v) throws Exception
{
return null;
}
@Override
public String marshal(InnerPayLoad<?> v) throws Exception
{
String value = TestCode.convertToXmlNoHeader(v.whichProfile,v.whichProfile.getClass());
// after converting value becomes
// <organization-information-profile>
// <name>Organization Information</name>
// </organization-information-profile>
return value;
}
}
但最终生成的 XML 与 organization-information-profile
<?xml version='1.0' encoding='UTF-8'?>
<request version="2.0" principle="11111" credentials="xxxxx">
<target name="TestAPI" operation="create">
<parameter>
<organization>
<qualified-name>newOrg</qualified-name>
<profile-set>
<innerPayLoad><organization-information-profile>
<name>Organization Information</name>
</organization-information-profile></innerPayLoad>
<name>testOrg</name>
</profile-set>
</organization>
</parameter>
</target>
</request>
是否可以删除 <innerPayLoad>
标记并仅使用 CustomAdaptor 编组函数插入 return 值?
感谢解决此问题的帮助和提示。
您不需要为 ProfileSet
中的各种配置文件类型编写自定义适配器。
相反,要处理这种混合的 XML 内容,规范的方法是这样的。
在你的 ProfileSet
class 你应该定义一个多态的 Java 属性 profile
可以取 <organization.information-profile>
的内容,
<connection-profile>
或 <user-information-profile>
元素。
(我在这里更喜欢名称 profile
而不是 innerPayload
)。
这些 XML 元素名称和 Java classes 之间的映射完成
通过使用 @XmlElements
注释。
@XmlAccessorType(XmlAccessType.FIELD)
public class ProfileSet {
@XmlElement(name = "name")
private String name;
// template to store different profile objects
@XmlElements({
@XmlElement(name = "organization-information-profile", type = OrganizationInfomationProfile.class),
@XmlElement(name = "connection-profile", type = ConnectionProfile.class),
@XmlElement(name = "user-information-profile", type = UserInformationProfile.class)
})
private Profile profile;
// default constructor used by JAXB unmarshaller
public ProfileSet() {
}
public ProfileSet(String name, Profile profile) {
this.name = name;
this.profile = profile;
}
}
您需要一个抽象的超级class Profile
只包含所有类型配置文件共有的属性:
@XmlAccessorType(XmlAccessType.FIELD)
public abstract class Profile {
@XmlElement
private String name;
@XmlElement(name = "attribute")
private List<Attribute> attributes;
}
你有一个子class OrganizationInformationProfile
代表
<organization-information-profile>
元素
@XmlAccessorType(XmlAccessType.FIELD)
public class OrganizationInfomationProfile extends Profile {
@XmlElement(name = "qualified-name")
private String qualifiedName;
@XmlElement(name = "last-name")
private String lastName;
@XmlElement(name = "address")
private String address;
// ... other properties
}
和另一个子class ConnectionProfile
代表<connection-profile>
元素
@XmlAccessorType(XmlAccessType.FIELD)
public class ConnectionProfile extends Profile {
@XmlElement(name = "service")
private Service service;
}
还有另一个子class UserInformationProfile
代表<user-information-profile>
元素。
通过使用上述方法,您可以解组 XML 示例 并在编组时再次获得相同的输出。