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”,这对我很有效。

我尝试过的解决方案 - 将每个字段包装在 iftry-catch 语句中(我不喜欢这样做,因为有数百个 XML 标记)。如果您有任何建议,请告诉我。谢谢。

【问题讨论】:

缺少很多 XML 注释。此外,前两个示例是相同的。另外,people.getPerson().get(i).getPet.getName 没有意义,因为getPet 是一个方法并返回一个列表? 您的 XML 示例也可能是错误的,因为&lt;pet&gt; 不包括&lt;name&gt;,而是DogCat 你是对的,如果这是一个不好的例子,我很抱歉,但我只是为了说明一个简单的例子而临时做的。这些字段可以替换为任何内容。至于 .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 &amp;&amp; persons.size() &gt; 0)。这样,它并不像您想象的那么冗长。这对你有帮助吗? 嘿@thatguy,我知道这个线程有点老了,但是可以用另一种方法使用 Optional 类(Java 8 中引入)吗?我找到了这个链接,它似乎与这个问题一致(oracle.com/technetwork/articles/java/…)。但是,我不太确定是否需要在 JAXB 中封装所有子项的每个成员属性?

以上是关于Java - 带有可选字段的 JAXB XML 解组的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 jaxb 将 xml 字符串解组为 java 对象

如何跳过字段并仅使用JAXB解组该字段的特定成员?

在将 XML 文件解组为对象后,如何让 JAXB 调用方法?

带有 java.lang.Object 字段的 JAXB 编组对象

JAXB - java xml解析

Java JAXB unmarshaller链接异常