Java - 带有可选字段的 JAXB XML 解组
Posted
技术标签:
【中文标题】Java - 带有可选字段的 JAXB XML 解组【英文标题】:Java - JAXB XML unmarshal with optional fields 【发布时间】:2017-04-06 10:35:44 【问题描述】:我在访问包含可选标签的未编组 XML 文件的字段时遇到问题。这是我为一个更复杂的情况编造的一个简单示例:
<people>
<persons>
<person>
<id>222</id>
<pets>
<pet>
<name age="2">Harry</name>
</pet>
<pet>
<name>Tiffany</name>
</pet>
</pets>
</person>
<person>
<id>111</id>
<pets>
<pet value="1"></pet>
</pets>
<spouse>Frank</spouse>
</person>
</persons>
</people>
请注意,第二个人有配偶,而第一个人没有。此外,第一人称的宠物有名字,而第二人称的宠物没有。名叫哈利的宠物也有年龄属性。我要说明的是,由于可选字段,我的 XML 文件可以包含不同的数据。
这是我的 JAXB 模型类:
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class People
@XmlElementWrapper
@XmlElement(name="person")
private List<Person> persons;
public List<Person> getPersons()
return persons;
public void setPersons(List<Person> persons)
this.persons= persons;
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Person
@XmlElement
private int id;
@XmlElementWrapper
@XmlElement(name="pet")
private List<Pet> pets;
@XmlElement
private String spouse;
//getters and setters
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class pet
@XmlAttribute
private int age;
@XmlValue
private String name;
//getters and setters
现在,假设我只想打印所有宠物的名字。
for (Person person : people.getPersons())
for (Pet pet : person.getPets())
System.out.println("Pet name: " + pet.getName());
如果缺少内部标签,我会收到NullPointerException
。有趣的是,如果您只是在人员层中搜索一个字段,它会跳过 XML 标记,就好像它不存在一样,例如:
for (Person person : people.getPersons())
System.out.println("Spouse: " + person.getSpouse());
即使第一个人没有配偶,上述命令也有效。它只提供字符串“null”,这对我很有效。
我尝试过的解决方案 - 将每个字段包装在 if
或 try-catch
语句中(我不喜欢这样做,因为有数百个 XML 标记)。如果您有任何建议,请告诉我。谢谢。
【问题讨论】:
缺少很多 XML 注释。此外,前两个示例是相同的。另外,people.getPerson().get(i).getPet.getName
没有意义,因为getPet
是一个方法并返回一个列表?
您的 XML 示例也可能是错误的,因为<pet>
不包括<name>
,而是Dog
和Cat
。
你是对的,如果这是一个不好的例子,我很抱歉,但我只是为了说明一个简单的例子而临时做的。这些字段可以替换为任何内容。至于 .get(i),我将更新我的帖子以包含一个 for 循环。谢谢。
【参考方案1】:
您示例中的NullPointerException
非常明显。
System.out.println("Pet name: " + people.getPerson().get(i).getPet().getName());
问题是,当你没有宠物时,getPet()
返回null
。随后,您在null
上调用getName()
,因此您得到了异常。在您的第二个示例中,不会发生执行,因为getSpouse()
返回null
,但System.out.println(...)
自动将其转换为字符串null。
尽管看起来很乏味,但在遍历列表或字段时,您必须明确检查 null
的属性。 永远不要对 try-catch
这样做!异常处理是一个繁重的机制,你不应该滥用这个概念来进行null
检查。
【讨论】:
感谢您的回复!我猜我最担心的事情成真了——检查所有字段以查看它们是否为空。但是,为每个字段创建一堆 if 语句不是不好的做法吗?任何可能的方法来创建一个单独的方法来检查这个? 问题是,您的字段是可选的。如果某个字段是必需的,您可以对其进行注释,以便 JAXB 检查这些字段并在null
上引发错误。但是,如果这些字段是可选的,您基本上会说:我期望一个具体的值(object != null
)或null
,两者都是 valid 输入。与其他所有应用程序一样,您在程序逻辑中处理这些数据,包括检查null
。 “为每个字段创建一堆 if 语句”是什么意思?你到底想通过这个实现什么?
我所说的“创建一堆 if 语句”的意思是包装 if 语句以查看“person”下的 XML 标记是否为空。因此,如果它为空,则可以跳过它。这是为了实现捕获 XML 中的所有数据字段。您*“明确检查 [所有] 属性是否为空”是什么意思?
通过明确检查null
我的意思是if (instance != null) ...
。在这种情况下,您必须检查 null
,因此您无法避免使用 if 语句。但是,如果您有一个列表 persons
并且您检查了 if (persons != null) ...
,那么您有一个 if 语句。然后你可以使用for (person personInList : persons) ...
。在循环体中,都是personInList != null
。如果您不想遍历列表,但要检查是否有人,只需使用if (persons != null && persons.size() > 0)
。这样,它并不像您想象的那么冗长。这对你有帮助吗?
嘿@thatguy,我知道这个线程有点老了,但是可以用另一种方法使用 Optional 类(Java 8 中引入)吗?我找到了这个链接,它似乎与这个问题一致(oracle.com/technetwork/articles/java/…)。但是,我不太确定是否需要在 JAXB 中封装所有子项的每个成员属性?以上是关于Java - 带有可选字段的 JAXB XML 解组的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 jaxb 将 xml 字符串解组为 java 对象
在将 XML 文件解组为对象后,如何让 JAXB 调用方法?