JAXB解组集合(集)

JAXB unmarshal Collection (Set)

我正在测试编组和解组以下 Java 个对象:

框架class:

@XmlRootElement (name = "framework")
@XmlAccessorType(XmlAccessType.FIELD)
@Entity
public class Framework implements Comparable<Framework>, Serializable {
    private static final long serialVersionUID = 1L;
    @XmlTransient
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String frameworkName;
    private String frameworkVersion;
    private String frameworkDescription;
    @XmlElementWrapper(name = "framework-domains")
    private Set<FrameworkDomain> frameworkDomainList = new HashSet<>();
    @XmlElementWrapper(name = "framework-comments")
    private Set<Comment> comments = new HashSet<>();

(仅包含 getter、setter 和一些函数,没有任何附加注释)

域class:

@XmlRootElement(name = "domain")
@XmlAccessorType(XmlAccessType.FIELD)
@Entity
public class FrameworkDomain implements Comparable<FrameworkDomain>, Serializable {

    private static final long serialVersionUID = 1L;
    @XmlTransient
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String domainName;
    private String domainDescription;
    @XmlElementWrapper(name = "domain-requirements")
    private Set<Requirement> domainRequirements = new HashSet<>();
    @XmlElementWrapper(name = "domain-comments")
    private Set<Comment> domainComments = new HashSet<>();

要求class:

@XmlRootElement(name = "requirement")
@XmlAccessorType(XmlAccessType.FIELD)
@Entity
public class Requirement implements Comparable<Requirement>, Serializable {

    private static final long serialVersionUID = 1L;
    @XmlTransient
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String requirementName;
    private String requirementDescription;
    private String requirementGuidance;
    @XmlElementWrapper(name = "sub-requirements")
    private Set<Requirement> requirementSubrequirementList = new HashSet<>();
    @XmlElementWrapper(name = "testing-procedures")
    private Set<TestingProcedure> requirementTestingProceduresList = new HashSet<>();
    @XmlElementWrapper(name = "requirement-comments")
    private Set<Comment> comments = new HashSet<>();

编组和解组代码:

public static String exportFramework(Framework f) {
    java.io.StringWriter s = new java.io.StringWriter();
    try {
        JAXBContext jc = JAXBContext.newInstance(Framework.class);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_ENCODING, "utf-8");
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

        marshaller.marshal(f, s);
    } catch (Exception ex) {
        ex.printStackTrace();
    }

    return s.toString();
}

public static Framework importFramework(java.io.InputStream xml) {
    intelicompliance.model.Framework f = null;

    try {
        JAXBContext jc = JAXBContext.newInstance(Framework.class);

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        f = (intelicompliance.model.Framework) unmarshaller.unmarshal(xml);
    } catch (JAXBException ex) {
        ex.printStackTrace();
    }

    return f;
}

当我编组我创建的对象时,它生成以下内容 XML:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<framework>
    <frameworkName>PCI DSS</frameworkName>
    <frameworkVersion>3.3</frameworkVersion>
    <frameworkDescription>The Payment Card Industry Data Security Standard (PCI DSS) is a proprietary information security standard for organizations that handle branded credit cards from the major card schemes including Visa, MasterCard, American Express, Discover, and JCB.</frameworkDescription>
    <framework-domains>
        <frameworkDomainList>
            <domainName>Domain 1a</domainName>
            <domainDescription>Build and Maintain a Secure Network and Systems</domainDescription>
            <domain-requirements></domain-requirements>
            <domain-comments></domain-comments>
        </frameworkDomainList>
        <frameworkDomainList>
            <domainName>Domain 2a</domainName>
            <domainDescription>Protect Cardholder Data</domainDescription>
            <domain-requirements/>
            <domain-comments/>
        </frameworkDomainList>
        <frameworkDomainList>
            <domainName>Domain 3a</domainName>
            <domainDescription>Maintain a Vulnerability Management Program</domainDescription>
            <domain-requirements/>
            <domain-comments/>
        </frameworkDomainList>
        <frameworkDomainList>
            <domainName>Domain 4a</domainName>
            <domainDescription>Implement Strong Access Control Measures</domainDescription>
            <domain-requirements/>
            <domain-comments/>
        </frameworkDomainList>
        <frameworkDomainList>
            <domainName>Domain 5a</domainName>
            <domainDescription>Regularly Monitor and Test Networks</domainDescription>
            <domain-requirements/>
            <domain-comments/>
        </frameworkDomainList>
        <frameworkDomainList>
            <domainName>Domain 6a</domainName>
            <domainDescription>Maintain an Information Security Policy</domainDescription>
            <domain-requirements/>
            <domain-comments/>
        </frameworkDomainList>
    </framework-domains>
    <framework-comments/>
</framework>

...这正是我所期待的。

但是,当我尝试将 XML 转换回对象时,集合中仅包含一个域 - 即导入(解组)过程忽略 XML第一个。

有人知道为什么吗?或者,我做错了什么?

更新 我通过更改取得了一些进展:

private Set<FrameworkDomain> frameworkDomainList = new HashSet<>();

至:

private List<FrameworkDomain> frameworkDomainList = new LinkedList<>();

现在按预期导入所有子元素。但是,我真的更喜欢使用 Set。

JAXB 对待 Set 和 List 的方式不同吗?

根据您对我的问题的回答:

我的意思是正确的是你确保每个不相同的对象都会 return 不同的散列。

你的问题来自于你正在解组的每个对象都具有相同的 hashCode,因此你的集合认为每个元素都是相等的,并且只保留一个(JAXB 处理的最后一个,顺便说一下第一个) .

您应该能够通过将没有 ID 的对象添加到 HashSet 中来确认此行为,只应保留最后一个。

它还解释了为什么当您切换到链表时您的列表会被填充。

使用不同的 hashCode 方法或编组 ID 来解决您的问题。

请记住,您应该始终与 hashCode() 方法一起重写 equals() 方法,并确保 a.equals(b) 和 a.hashCode == b.hashCode 始终产生相同的结果。