我正在编写一个基于SOAP的Web服务和一个客户端应用程序,它使用从XML文件读取的导入数据来调用Web服务。 我使用wsimport从WSDL文件为客户端生成必要的类。
导入文件“Books.xml”必须如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<books>
<book isbn="aaa" title="Aaa" subtitle="aaa" description="aaa" pages="25" language="german">
<publisher name="a" postcode="a" countrycode="a" />
<authors>
<author firstname="a" lastname="a" birthday="1999-01-01" />
</authors>
</book>
</books>
我使用自创的包装类Books.java作为@XmlRootElement
来解组Books列表。解组XML文件时,我无法获取&lt; authors&gt;的子项。元素,因此由unmarshaller创建的Book对象永远不会有任何作者。
这是用于解组XML文件的代码,然后编组生成的Books对象:
JAXBContext jaxbContext = JAXBContext.newInstance(Books.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
Source source = new StreamSource(new File(xmlPath));
JAXBElement<Books> jaxbBooks = unmarshaller.unmarshal(source, Books.class);
Books books = jaxbBooks.getValue();
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_ENCODING, "ISO-8859-1");
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(books, System.out); // just for debug purposes
产生以下结果:
<books xmlns:ns2="http://technikumwien.at/">
<book isbn="aaa" title="Aaa" subtitle="aaa" description="aaa" pages="25" language="german">
<ns2:publisher publisherId="0" name="a" postcode="a" countrycode="a"/>
<ns2:authors/>
</book>
</books>
在我的服务器的Book.java中,我使用@XmlElementWrapper
来包装作者列表:
@XmlElementWrapper(name="authors")
@XmlElement(name="author")
private List<Author> authors;
我看了一下wsimport生成的客户端的Book.java - 作者列表已被转换为嵌套的静态类:
/**
* <p>Java-Klasse für anonymous complex type.
*
* <p>Das folgende Schemafragment gibt den erwarteten Content an, der in dieser Klasse enthalten ist.
*
* <pre>
* <complexType>
* <complexContent>
* <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* <sequence>
* <element ref="{http://technikumwien.at/}author" maxOccurs="unbounded" minOccurs="0"/>
* </sequence>
* </restriction>
* </complexContent>
* </complexType>
* </pre>
*
*
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"author"
})
public static class Authors {
protected List<Author> author;
/**
* Gets the value of the author property.
*
* <p>
* This accessor method returns a reference to the live list,
* not a snapshot. Therefore any modification you make to the
* returned list will be present inside the JAXB object.
* This is why there is not a <CODE>set</CODE> method for the author property.
*
* <p>
* For example, to add a new item, do as follows:
* <pre>
* getAuthor().add(newItem);
* </pre>
*
*
* <p>
* Objects of the following type(s) are allowed in the list
* {@link Author }
*
*
*/
public List<Author> getAuthor() {
if (author == null) {
author = new ArrayList<Author>();
}
return this.author;
}
}
我注意到@XmlType
注释的name属性已经创建为空,如果我用@XmlType(name="")
替换@XmlType(name="authors")
,则取消编组成功捕获Book的Author子元素:
<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>
<books xmlns:ns2="http://technikumwien.at/">
<book isbn="aaa" title="Aaa" subtitle="aaa" description="aaa" pages="25" language="german">
<ns2:publisher publisherId="0" name="a" postcode="a" countrycode="a"/>
<ns2:authors>
<ns2:author authorId="0" firstname="a" lastname="a" birthday="1999-01-01"/>
</ns2:authors>
</book>
</books>
但是,我不想修改WSIMPORT产生的类别!
所以我通过将XML解析为DOM树来解决问题,使用TreeWalker遍历DOM树。对于每个节点,如果节点是&lt; Book&gt; element我解组了这个“Book node”并将它推送到一个单独的Books列表中。如果遍历的节点是&lt; Author&gt; element,我解组了“作者节点”并将作者对象添加到书籍列表中的最后一个Book对象:
private static final List<Book> traverseLevel(TreeWalker walker, Unmarshaller unmarshaller, List<Book> bookList) throws JAXBException {
Node parent = walker.getCurrentNode();
if (parent.getNodeName().equals("book"))
{
JAXBElement<Book> bookElem = unmarshaller.unmarshal(parent, Book.class);
Book book = bookElem.getValue();
bookList.add(book);
}
if (parent.getNodeName().equals("author"))
{
JAXBElement<Author> authorElem = unmarshaller.unmarshal(parent, Author.class);
Author author = authorElem.getValue();
bookList.get(bookList.size() - 1).getAuthors().getAuthor().add(author);
}
for (Node n = walker.firstChild(); n != null; n = walker.nextSibling()) {
traverseLevel(walker, unmarshaller, bookList);
}
walker.setCurrentNode(parent);
return bookList;
}
这个解决方案有效,但我发现它非常不灵活,我认为必须有一种更简单的方法来成功解组Books的嵌套Author元素,而无需手动遍历DOM树。
我还必须使用以下修复https://stackoverflow.com/a/7736235/4716861来摆脱命名空间前缀问题,所以在我的服务器的package-info.java中我写道:
@XmlSchema(
namespace = "http://technikumwien.at/",
elementFormDefault = XmlNsForm.QUALIFIED,
xmlns = {
@XmlNs(prefix="", namespaceURI="http://technikumwien.at/")
}
)
@XmlJavaTypeAdapters({
@XmlJavaTypeAdapter(type=LocalDate.class, value=LocalDateXmlAdapter.class)
})
package at.technikumwien;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters;
import java.time.LocalDate;
Wsdl文件的一部分(启动WildFly时自动生成):
<xs:complexType name="book">
<xs:sequence>
<xs:element minOccurs="0" ref="publisher"></xs:element>
<xs:element minOccurs="0" name="authors">
<xs:complexType>
<xs:sequence>
<xs:element maxOccurs="unbounded" minOccurs="0" ref="author"></xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="bookId" type="xs:long"></xs:attribute>
<xs:attribute name="isbn" type="xs:string"></xs:attribute>
<xs:attribute name="title" type="xs:string"></xs:attribute>
<xs:attribute name="subtitle" type="xs:string"></xs:attribute>
<xs:attribute name="description" type="xs:string"></xs:attribute>
<xs:attribute name="pages" type="xs:int" use="required"></xs:attribute>
<xs:attribute name="language" type="xs:string"></xs:attribute>
</xs:complexType>
<xs:complexType name="publisher">
<xs:sequence></xs:sequence>
<xs:attribute name="publisherId" type="xs:long" use="required"></xs:attribute>
<xs:attribute name="name" type="xs:string"></xs:attribute>
<xs:attribute name="postcode" type="xs:string"></xs:attribute>
<xs:attribute name="countrycode" type="xs:string"></xs:attribute>
</xs:complexType>
<xs:complexType name="author">
<xs:sequence></xs:sequence>
<xs:attribute name="authorId" type="xs:long" use="required"></xs:attribute>
<xs:attribute name="firstname" type="xs:string"></xs:attribute>
<xs:attribute name="lastname" type="xs:string"></xs:attribute>
<xs:attribute name="birthday" type="xs:string"></xs:attribute>
</xs:complexType>
服务器的Book.java
@Entity
@Table(name="book")
@NamedQueries({
@NamedQuery(name="Book.selectAll", query="SELECT b FROM Book b"),
@NamedQuery(name="Book.selectByTitle", query="SELECT b FROM Book b WHERE b.title like CONCAT('%', :title ,'%')")
})
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@XmlAttribute(name="bookId")
private Long bookId;
@XmlAttribute(name="isbn")
private String isbn;
@XmlAttribute(name="title")
private String title;
@XmlAttribute(name="subtitle")
private String subtitle;
@XmlAttribute(name="description")
private String description;
@XmlAttribute(name="pages")
private int pages;
@XmlAttribute(name="language")
private String language;
@ManyToOne(optional = false)
@NotNull
@XmlElement(name="publisher")
private Publisher publisher;
@ManyToMany(mappedBy = "books", fetch = FetchType.EAGER)
@NotEmpty
@XmlElementWrapper(name="authors")
@XmlElement(name="author")
private List<Author> authors;
服务器的Author.java
@Entity
@Table(name="author")
@NamedQueries({
@NamedQuery(name="Author.selectAll", query="SELECT a FROM Author a"),
@NamedQuery(name="Author.checkExists", query="SELECT a FROM Author a WHERE a.firstname = :firstname AND a.lastname = :lastname AND a.birthday = :birthday")
})
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Author {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@XmlAttribute(name="authorId")
private long authorId;
@XmlAttribute(name="firstname")
private String firstname;
@XmlAttribute(name="lastname")
private String lastname;
@XmlAttribute(name="birthday")
@Convert(converter = LocalDatePersistenceConverter.class)
private LocalDate birthday;
@ManyToMany
@XmlTransient
private List<Book> books = new ArrayList<Book>();
问题是我遇到了预期的行为还是我错过了什么? unmarshaller是否应该能够使用wsimport生成的类默认捕获XML层次结构的所有子级?
张贴了这么多代码,但也许它可能与解决问题有关。希望任何人都能暗示我走向正确的方向。谢谢!