Java feed XML 使用 JAXB 绑定命名空间
Java feed XML with namespace bind using JAXB
我正在尝试绑定下面示例 xml 文件中的电子邮件元素,但无法正确绑定。我得到空值。
样本XML
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:api="http://www.example.com/publications/api">
<api:schema-version>5.3</api:schema-version>
<category scheme="http://www.example.com/publications/atom/feeds/" term="item" label="Item" />
<id>tag:elements@abcd,5.15:/proted-api/v5.5/feeds/users/12312</id>
<entry>
<id>tag:elements@abcd,5.15:/proted-api/v5.5/users/12312</id>
<category scheme="http://www.example.com/publications/atom/entries/" term="item" label="Item" />
<content type="xhtml">
<api:object category="user" id="12312" proprietary-id="abcd123">
<api:last-name>Jo</api:last-name>
<api:first-name>Deo</api:first-name>
<api:email-address>jode@example.com</api:email-address>
</api:object>
</entry>
</feed>
Feed.java
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Feed {
@XmlElement(name="entry")
private Entry entry;
public Entry getEntry() {
return entry;
}
public void setEntry(Entry entry) {
this.entry = entry;
}
}
Entry.java
@XmlAccessorType(XmlAccessType.FIELD)
public class Entry {
@XmlElement(name="object", namespace = "http://www.example.com/publications/api")
private Object object;
public Object getObject() {
return object;
}
public void setObject(Object object) {
this.object = object;
}
}
Object.java
@XmlAccessorType(XmlAccessType.FIELD)
public class Object {
@XmlElement(name="email-address",namespace = "http://www.example.com/publications/api")
private String email;
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
测试代码
//code here to read
Feed feed = response.getBody();
System.out.println("object email = " +feed.getEntry().getObject().getEmail());
完整 XML 文件
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:api="http://www.example.com/publications/api">
<api:schema-version>5.5</api:schema-version>
<category scheme="http://www.example.com/publications/atom/feeds/" term="item" label="Item" />
<id>tag:elements@Test,5.15:/secure-api/v5.5/feeds/users/676</id>
<updated>2019-06-17T09:04:39.87+01:00</updated>
<generator uri="https://test.com/" version="5.15">Example Elements</generator>
<icon>https://test.com:8091/secure-api/v5.5/Example.ico</icon>
<rights>This data is the property of the Organisation, and can only be used with permission.</rights>
<subtitle>This feed represents a single user.</subtitle>
<link type="application/atom+xml" rel="self" href="https://test.com:8091/secure-api/v5.5/users/676" />
<title>John deo</title>
<author>
<name>Example Elements at Test PROD</name>
</author>
<entry>
<id>tag:elements@Test,5.15:/secure-api/v5.5/users/676</id>
<category scheme="http://www.example.com/publications/atom/entries/" term="item" label="Item" />
<updated>2019-06-17T09:04:39.87+01:00</updated>
<link type="application/atom+xml" rel="alternate" href="https://test.com:8091/secure-api/v5.5/users/676" />
<title>John deo</title>
<content type="xhtml">
<div xmlns="http://www.w3.org/1999/xhtml">
<p>User</p>
<a href="https://test.com:8091/secure-api/v5.5/users/676/photo?type=profile">Photo</a>
<p>
<a href="https://test.com:8091/secure-api/v5.5/users/676/relationships">Relationships</a>
with other data
</p>
</div>
</content>
<api:object category="user" id="676" proprietary-id="abcd1247" username="abcd1247" last-affected-when="2019-06-17T09:04:39.87+01:00" last-modified-when="2018-11-29T10:28:43.403+00:00" href="https://test.com:8091/secure-api/v5.5/users/676" created-when="2010-05-04T09:49:46.507+01:00" type-id="1" type="person">
<!-- User type 1 is "person" -->
<api:ever-approved>true</api:ever-approved>
<api:is-public>false</api:is-public>
<api:is-login-allowed>true</api:is-login-allowed>
<api:title>Prof</api:title>
<api:initials>Jo</api:initials>
<api:last-name>John</api:last-name>
<api:first-name>Deo</api:first-name>
<api:email-address>John.deo@example.com</api:email-address>
<api:known-as>Liz</api:known-as>
<api:primary-group-descriptor>ABCS</api:primary-group-descriptor>
<api:arrive-date>2009-10-05</api:arrive-date>
<api:user-search-settings>
<api:default>
</api:default>
</api:user-search-settings>
<api:records>
<api:record format="native" id="3245433412" source-id="1" source-name="manual" source-display-name="Manual">
<api:native />
</api:record>
</api:records>
<api:fields />
<api:relationships href="https://test.com:8091/secure-api/v5.5/users/676/relationships" />
<api:user-identifier-associations user-id="676" proprietary-id="abcd1247" username="abcd1247">
</api:user-identifier-associations>
</api:object>
</entry>
</feed>
问题是您的 POJO 结构与 xml 不匹配(正如我们最终发现的那样,RestTemplate 没有使用 JAXB 注释,请参阅下面的编辑 4)。
让我们解决这个问题,以便您能够正确解组。
大部分字段都在使用 http://www.w3.org/2005/Atom
命名空间。我们将使用 package-info 而不是在每个元素中添加它。因此,在 classes 的包中创建一个文件 package-info.java,内容为:
@javax.xml.bind.annotation.XmlSchema(namespace = "http://www.w3.org/2005/Atom", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package the.package.of.your.classes;
然后让我们修复 Feed。你的 xml 有你的 class 没有的元素,这是一个问题,因为它遇到了意想不到的元素。饲料将是:
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Feed {
@XmlElement(name="schema-version", namespace = "http://www.example.com/publications/api")
private String schemaVersion;
@XmlElement
private String category;
@XmlElement
private String id;
@XmlElement(name="entry")
private Entry entry;
}
请注意,schema-version 的命名空间与我们在 package-info 中的命名空间不同,因此我们在注释中明确覆盖了它。
现在,这被简化了。例如,类别有 xml 个属性,例如术语和标签。如果你想得到这个信息,你应该创建一个 class 来表示类别而不是字符串。但如果你不这样做,像这样的 Feed 允许我们继续解组。接下来是条目:
@XmlAccessorType(XmlAccessType.FIELD)
public class Entry {
@XmlElement
private String id;
@XmlElement
private String category;
@XmlElement
private String content;
@XmlElement(name="object", namespace = "http://www.example.com/publications/api")
private ApiObject object;
}
再次使用字符串忽略属性。我将 Object 更改为名为 ApiObject 的 class,它具有与 xml 匹配的结构。它看起来像这样:
@XmlAccessorType(XmlAccessType.FIELD)
public class ApiObject {
@XmlElement(name= "last-name", namespace = "http://www.example.com/publications/api")
private String lastName;
@XmlElement(name = "first-name", namespace = "http://www.example.com/publications/api")
private String firstName;
@XmlElement(name = "email-address", namespace = "http://www.example.com/publications/api")
private String emailAddress;
}
最后一件事,xml。无效 xml,标签 "content" 在粘贴的示例中未关闭。我将其更改为 <content type="xhtml" />
以测试我的解决方案。
而且确实可以正常工作和解组:)
编辑回复:
对于问题 1,您现在可以在每个不直接指定名称空间的元素中添加名称空间。
对于问题 2,您可以使用此处建议的方法忽略未知元素:JAXB Ignore 'extra' elements from Response XML
Feed 例如:
@XmlRootElement(namespace = "http://www.w3.org/2005/Atom")
@XmlAccessorType(XmlAccessType.FIELD)
public class Feed {
@XmlAnyElement(lax = true)
private List<Object> anything;
@XmlElement(name="entry", namespace = "http://www.w3.org/2005/Atom")
private Entry entry;
}
编辑 2:
使用您尝试解组的 xml 并执行如下操作:
try {
File file = new File("/path/to/your/file.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(YouRootClass.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
jaxbUnmarshaller.setEventHandler(
new ValidationEventHandler() {
public boolean handleEvent(ValidationEvent event ) {
throw new RuntimeException(event.getMessage(),
event.getLinkedException());
}
});
YouRootClass pojo = (YouRootClass) jaxbUnmarshaller.unmarshal(file);
} catch (JAXBException e) {
e.printStackTrace();
}
您的 xml 和 POJO 之间的任何不匹配都将作为异常抛出,您可以按照自己的方式进行更正
编辑 3:
供稿
@XmlRootElement(namespace = "http://www.w3.org/2005/Atom")
@XmlAccessorType(XmlAccessType.FIELD)
public class Feed {
@XmlAnyElement(lax = true)
private List<Object> anything;
@XmlElement(name="entry", namespace = "http://www.w3.org/2005/Atom")
private Entry entry;
}
条目
@XmlAccessorType(XmlAccessType.FIELD)
public class Entry {
@XmlAnyElement(lax = true)
private List<Object> anything;
@XmlElement(name="object", namespace = "http://www.example.com/publications/api")
private ApiObject object;
}
Api对象:
@XmlAccessorType(XmlAccessType.FIELD)
public class ApiObject {
@XmlAnyElement(lax = true)
private List<Object> anything;
@XmlElement(name= "last-name", namespace = "http://www.example.com/publications/api")
private String lastName;
@XmlElement(name = "first-name", namespace = "http://www.example.com/publications/api")
private String firstName;
@XmlElement(name = "email-address", namespace = "http://www.example.com/publications/api")
private String emailAddress;
}
编辑 4:
在您的 github 项目中,class 创建 RestTemplate 后进行测试(第 31 行)添加:
List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
Jaxb2RootElementHttpMessageConverter jaxb2RootElementHttpMessageConverter = new Jaxb2RootElementHttpMessageConverter();
messageConverters.add(jaxb2RootElementHttpMessageConverter);
rest.setMessageConverters(messageConverters);
我正在尝试绑定下面示例 xml 文件中的电子邮件元素,但无法正确绑定。我得到空值。
样本XML
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:api="http://www.example.com/publications/api">
<api:schema-version>5.3</api:schema-version>
<category scheme="http://www.example.com/publications/atom/feeds/" term="item" label="Item" />
<id>tag:elements@abcd,5.15:/proted-api/v5.5/feeds/users/12312</id>
<entry>
<id>tag:elements@abcd,5.15:/proted-api/v5.5/users/12312</id>
<category scheme="http://www.example.com/publications/atom/entries/" term="item" label="Item" />
<content type="xhtml">
<api:object category="user" id="12312" proprietary-id="abcd123">
<api:last-name>Jo</api:last-name>
<api:first-name>Deo</api:first-name>
<api:email-address>jode@example.com</api:email-address>
</api:object>
</entry>
</feed>
Feed.java
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Feed {
@XmlElement(name="entry")
private Entry entry;
public Entry getEntry() {
return entry;
}
public void setEntry(Entry entry) {
this.entry = entry;
}
}
Entry.java
@XmlAccessorType(XmlAccessType.FIELD)
public class Entry {
@XmlElement(name="object", namespace = "http://www.example.com/publications/api")
private Object object;
public Object getObject() {
return object;
}
public void setObject(Object object) {
this.object = object;
}
}
Object.java
@XmlAccessorType(XmlAccessType.FIELD)
public class Object {
@XmlElement(name="email-address",namespace = "http://www.example.com/publications/api")
private String email;
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
测试代码
//code here to read
Feed feed = response.getBody();
System.out.println("object email = " +feed.getEntry().getObject().getEmail());
完整 XML 文件
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:api="http://www.example.com/publications/api">
<api:schema-version>5.5</api:schema-version>
<category scheme="http://www.example.com/publications/atom/feeds/" term="item" label="Item" />
<id>tag:elements@Test,5.15:/secure-api/v5.5/feeds/users/676</id>
<updated>2019-06-17T09:04:39.87+01:00</updated>
<generator uri="https://test.com/" version="5.15">Example Elements</generator>
<icon>https://test.com:8091/secure-api/v5.5/Example.ico</icon>
<rights>This data is the property of the Organisation, and can only be used with permission.</rights>
<subtitle>This feed represents a single user.</subtitle>
<link type="application/atom+xml" rel="self" href="https://test.com:8091/secure-api/v5.5/users/676" />
<title>John deo</title>
<author>
<name>Example Elements at Test PROD</name>
</author>
<entry>
<id>tag:elements@Test,5.15:/secure-api/v5.5/users/676</id>
<category scheme="http://www.example.com/publications/atom/entries/" term="item" label="Item" />
<updated>2019-06-17T09:04:39.87+01:00</updated>
<link type="application/atom+xml" rel="alternate" href="https://test.com:8091/secure-api/v5.5/users/676" />
<title>John deo</title>
<content type="xhtml">
<div xmlns="http://www.w3.org/1999/xhtml">
<p>User</p>
<a href="https://test.com:8091/secure-api/v5.5/users/676/photo?type=profile">Photo</a>
<p>
<a href="https://test.com:8091/secure-api/v5.5/users/676/relationships">Relationships</a>
with other data
</p>
</div>
</content>
<api:object category="user" id="676" proprietary-id="abcd1247" username="abcd1247" last-affected-when="2019-06-17T09:04:39.87+01:00" last-modified-when="2018-11-29T10:28:43.403+00:00" href="https://test.com:8091/secure-api/v5.5/users/676" created-when="2010-05-04T09:49:46.507+01:00" type-id="1" type="person">
<!-- User type 1 is "person" -->
<api:ever-approved>true</api:ever-approved>
<api:is-public>false</api:is-public>
<api:is-login-allowed>true</api:is-login-allowed>
<api:title>Prof</api:title>
<api:initials>Jo</api:initials>
<api:last-name>John</api:last-name>
<api:first-name>Deo</api:first-name>
<api:email-address>John.deo@example.com</api:email-address>
<api:known-as>Liz</api:known-as>
<api:primary-group-descriptor>ABCS</api:primary-group-descriptor>
<api:arrive-date>2009-10-05</api:arrive-date>
<api:user-search-settings>
<api:default>
</api:default>
</api:user-search-settings>
<api:records>
<api:record format="native" id="3245433412" source-id="1" source-name="manual" source-display-name="Manual">
<api:native />
</api:record>
</api:records>
<api:fields />
<api:relationships href="https://test.com:8091/secure-api/v5.5/users/676/relationships" />
<api:user-identifier-associations user-id="676" proprietary-id="abcd1247" username="abcd1247">
</api:user-identifier-associations>
</api:object>
</entry>
</feed>
问题是您的 POJO 结构与 xml 不匹配(正如我们最终发现的那样,RestTemplate 没有使用 JAXB 注释,请参阅下面的编辑 4)。
让我们解决这个问题,以便您能够正确解组。
大部分字段都在使用 http://www.w3.org/2005/Atom
命名空间。我们将使用 package-info 而不是在每个元素中添加它。因此,在 classes 的包中创建一个文件 package-info.java,内容为:
@javax.xml.bind.annotation.XmlSchema(namespace = "http://www.w3.org/2005/Atom", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package the.package.of.your.classes;
然后让我们修复 Feed。你的 xml 有你的 class 没有的元素,这是一个问题,因为它遇到了意想不到的元素。饲料将是:
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Feed {
@XmlElement(name="schema-version", namespace = "http://www.example.com/publications/api")
private String schemaVersion;
@XmlElement
private String category;
@XmlElement
private String id;
@XmlElement(name="entry")
private Entry entry;
}
请注意,schema-version 的命名空间与我们在 package-info 中的命名空间不同,因此我们在注释中明确覆盖了它。
现在,这被简化了。例如,类别有 xml 个属性,例如术语和标签。如果你想得到这个信息,你应该创建一个 class 来表示类别而不是字符串。但如果你不这样做,像这样的 Feed 允许我们继续解组。接下来是条目:
@XmlAccessorType(XmlAccessType.FIELD)
public class Entry {
@XmlElement
private String id;
@XmlElement
private String category;
@XmlElement
private String content;
@XmlElement(name="object", namespace = "http://www.example.com/publications/api")
private ApiObject object;
}
再次使用字符串忽略属性。我将 Object 更改为名为 ApiObject 的 class,它具有与 xml 匹配的结构。它看起来像这样:
@XmlAccessorType(XmlAccessType.FIELD)
public class ApiObject {
@XmlElement(name= "last-name", namespace = "http://www.example.com/publications/api")
private String lastName;
@XmlElement(name = "first-name", namespace = "http://www.example.com/publications/api")
private String firstName;
@XmlElement(name = "email-address", namespace = "http://www.example.com/publications/api")
private String emailAddress;
}
最后一件事,xml。无效 xml,标签 "content" 在粘贴的示例中未关闭。我将其更改为 <content type="xhtml" />
以测试我的解决方案。
而且确实可以正常工作和解组:)
编辑回复:
对于问题 1,您现在可以在每个不直接指定名称空间的元素中添加名称空间。
对于问题 2,您可以使用此处建议的方法忽略未知元素:JAXB Ignore 'extra' elements from Response XML
Feed 例如:
@XmlRootElement(namespace = "http://www.w3.org/2005/Atom")
@XmlAccessorType(XmlAccessType.FIELD)
public class Feed {
@XmlAnyElement(lax = true)
private List<Object> anything;
@XmlElement(name="entry", namespace = "http://www.w3.org/2005/Atom")
private Entry entry;
}
编辑 2:
使用您尝试解组的 xml 并执行如下操作:
try {
File file = new File("/path/to/your/file.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(YouRootClass.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
jaxbUnmarshaller.setEventHandler(
new ValidationEventHandler() {
public boolean handleEvent(ValidationEvent event ) {
throw new RuntimeException(event.getMessage(),
event.getLinkedException());
}
});
YouRootClass pojo = (YouRootClass) jaxbUnmarshaller.unmarshal(file);
} catch (JAXBException e) {
e.printStackTrace();
}
您的 xml 和 POJO 之间的任何不匹配都将作为异常抛出,您可以按照自己的方式进行更正
编辑 3:
供稿
@XmlRootElement(namespace = "http://www.w3.org/2005/Atom")
@XmlAccessorType(XmlAccessType.FIELD)
public class Feed {
@XmlAnyElement(lax = true)
private List<Object> anything;
@XmlElement(name="entry", namespace = "http://www.w3.org/2005/Atom")
private Entry entry;
}
条目
@XmlAccessorType(XmlAccessType.FIELD)
public class Entry {
@XmlAnyElement(lax = true)
private List<Object> anything;
@XmlElement(name="object", namespace = "http://www.example.com/publications/api")
private ApiObject object;
}
Api对象:
@XmlAccessorType(XmlAccessType.FIELD)
public class ApiObject {
@XmlAnyElement(lax = true)
private List<Object> anything;
@XmlElement(name= "last-name", namespace = "http://www.example.com/publications/api")
private String lastName;
@XmlElement(name = "first-name", namespace = "http://www.example.com/publications/api")
private String firstName;
@XmlElement(name = "email-address", namespace = "http://www.example.com/publications/api")
private String emailAddress;
}
编辑 4:
在您的 github 项目中,class 创建 RestTemplate 后进行测试(第 31 行)添加:
List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
Jaxb2RootElementHttpMessageConverter jaxb2RootElementHttpMessageConverter = new Jaxb2RootElementHttpMessageConverter();
messageConverters.add(jaxb2RootElementHttpMessageConverter);
rest.setMessageConverters(messageConverters);