JAXB - 如何以两种方式解析元素

时间:2014-02-13 19:49:30

标签: java xml jpa jaxb moxy

我正在尝试解析一个具有到其他类的多个双向连接的类。为了防止无限循环问题和其他原因导致我想以自定义方式解析一些属性。

即我有类MovieImpl(实现电影界面),其中包含一个actor列表(PersonImpl)和一个导演(PersonImpl)字段(当然还有其他字段):

@XmlRootElement
@XmlSeeAlso({ PersonImpl.class })
@Entity
public class MovieImpl implements Movie {

    @Id
    @Column(name = "movieId", unique = true)
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    int _id;

    ...

    @ManyToOne(fetch = FetchType.LAZY, targetEntity = PersonImpl.class)
    @JoinColumn(name = "directorId")
    Person _director;

    ...

    @ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, targetEntity = PersonImpl.class)
    @JoinTable(name = "movie_has_actors", joinColumns = { @JoinColumn(name = "movieId", nullable = false) }, inverseJoinColumns = { @JoinColumn(name = "personId", nullable = false) })
    Collection<Person> _actors;

    ...
}

我的PersonImpl类有一个电影列表:

@XmlRootElement
@XmlSeeAlso({ MovieImpl.class })
@Entity
public class PersonImpl implements Person {

    ...
    private int _id;
    private String _name;

    @ManyToMany(mappedBy = "_actors", targetEntity = MovieImpl.class)
    private Collection<Movie> _movies;

    ...

}

因此,当从电影对象渲染时,jaxb只能显示actor的名称和id。 而另一方面:当从人物对象中渲染时,只显示电影中的其他一些字段吗?

即:

<movie>
    <id>82303482</id>
    ...
    <director>
        <id>340980</id>
        <name>xyz</name>
    </director>
    <actors>
        <actor>
            <id>479834</id>
            <name>xyz</name>
        </actor>
        <actor>
            <id>3434234</id>
            <name>abc</name>
        </actor>
    ...
</movie>


<actor>
    <id>34843</id>
    <name>abc</name>
    ...
    <movies>
        <movie>
            <id>3489</id>
            <title>asdf</title>
            ...
        </movie>
        <movie>
            <id>4534534</id>
            <title>qwer</title>
            ...
        </movie>
    </movies>
</actor>

提前感谢您的帮助。

更新 当我将@XmlInverseReference注释添加到字段时,它只给出了类似的内容:

...
<_actors>
    com.company.package...PersonImpl@2450c3
</_actors>
<_actors>
    com.company.package...PersonImpl@4e98166
</_actors>
<actors>
    com.company.package...PersonImpl@2450c3
</actors>
<actors>
    com.company.package...PersonImpl@4e98166
</actors>
...
  1. 为什么演员(_actor和演员)有2个字段?
  2. 为什么不显示演员的领域?

2 个答案:

答案 0 :(得分:1)

JAXB无法以多种方式将对象扩展为XML。如果您有多个实体集合,相互链接,则最佳策略是将每个实体对象展开一次,并且只展开一次。对其他实体的引用应使用IDREF进行注释。当然,这需要每个对象都有一个ID,但合成序数可以使用。

如果您发布的XML结构是其他程序所需的,您可以考虑使用XSLT处理主要输出及其IDREF。

请参阅我的JAXB tutorial

中的最后一部分

答案 1 :(得分:1)

因为根据您的问号标签,您使用的是MOXy:

MOXy允许通过@XmlInverseReference扩展进行双向映射(您需要使用EclipseLink 2.5.0或更高版本才能在两个方向上写入关系):

MOXy还允许您使用对象图扩展来编组数据的子集:


注意

下面是答案,但似乎有@XmlElementWrapper@XmlInverseReference的编组错误。

Java模型

<强> MovieImpl

import java.util.Collection;
import javax.persistence.*;
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.*;

@XmlRootElement(name="movie")
@XmlAccessorType(XmlAccessType.FIELD)
@XmlNamedObjectGraph(
        name="partial",
        attributeNodes={
            @XmlNamedAttributeNode("_id"),
            @XmlNamedAttributeNode(value="_director", subgraph="partial"),
            @XmlNamedAttributeNode(value="_actors", subgraph="partial")
        },
        subgraphs={
            @XmlNamedSubgraph(
                name="location",
                attributeNodes = {
                    @XmlNamedAttributeNode("_id"),
                    @XmlNamedAttributeNode("_name")
                }
            )
        }
    )
@Entity
public class MovieImpl implements Movie {

    @XmlElement(name="id")
    @Id
    @Column(name = "movieId", unique = true)
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    int _id;

    @XmlElement(name="director", type=PersonImpl.class)
    @ManyToOne(fetch = FetchType.LAZY, targetEntity = PersonImpl.class)
    @JoinColumn(name = "directorId")
    Person _director;

    @XmlElementWrapper(name="actors")
    @XmlElement(name="actor", type=PersonImpl.class)
    @XmlInverseReference(mappedBy="_actors")
    @ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, targetEntity = PersonImpl.class)
    @JoinTable(name = "movie_has_actors", joinColumns = { @JoinColumn(name = "movieId", nullable = false) }, inverseJoinColumns = { @JoinColumn(name = "personId", nullable = false) })
    Collection<Person> _actors;

    public Collection<Person> getActors() {
        return _actors;
    }

}

<强> PersonImpl的

import java.util.Collection;
import javax.persistence.*;
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.*;

@XmlRootElement(name="actor")
@XmlAccessorType(XmlAccessType.FIELD)
@XmlNamedObjectGraph(
        name="partial",
        attributeNodes={
            @XmlNamedAttributeNode("_id"),
            @XmlNamedAttributeNode("_name"),
            @XmlNamedAttributeNode(value="_movies", subgraph="partial")
        },
        subgraphs={
            @XmlNamedSubgraph(
                name="location",
                attributeNodes = {
                    @XmlNamedAttributeNode("_id"),
                    @XmlNamedAttributeNode("_name")
                }
            )
        }
    )
@Entity
public class PersonImpl implements Person {

    @XmlElement(name="id")
    private int _id;

    @XmlElement(name="name")
    private String _name;

    @XmlElementWrapper(name="movies")
    @XmlElement(name="movie", type=MovieImpl.class)
    @XmlInverseReference(mappedBy="_actors")
    @ManyToMany(mappedBy = "_actors", targetEntity = MovieImpl.class)
    private Collection<Movie> _movies;

}

演示代码

<强>演示

import java.io.File;
import javax.xml.bind.*;
import org.eclipse.persistence.jaxb.MarshallerProperties;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(MovieImpl.class);

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        File xml = new File("src/forum21764131/input.xml");
        Movie movie = (Movie) unmarshaller.unmarshal(xml);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.setProperty(MarshallerProperties.OBJECT_GRAPH, "partial");
        marshaller.marshal(movie.getActors().toArray()[0], System.out);
    }

}