java 使用 JAXB 解组时将空元素转换为 null
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4181120/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Transforming empty element into null when unmarshalling with JAXB
提问by torngat
A class is defined with the following JAXB annotation:
使用以下 JAXB 注释定义类:
class Course {
@XmlElement (name = "book")
List<Book> requiredBooks = new ArrayList<Book>();
When unmarshalling an XML document that contains this
解组包含此的 XML 文档时
<course>
...
<book/>
</course>
I end up with a Book added to the list, with all of its attributes set to null. I don't control the XML input. How can I prevent this empty book from being added? I tried intercepting in set..() or add..() methods, but turns out JAXB bypasses setters when dealing with collections. Any suggestions?
我最终将 Book 添加到列表中,并将其所有属性设置为 null。我不控制 XML 输入。如何防止添加这本空书?我尝试在 set..() 或 add..() 方法中进行拦截,但结果是 JAXB 在处理集合时绕过了 setter。有什么建议?
采纳答案by torngat
Here's a solution that doeswork. Again, elegance was left behind.
这里有一个解决方案做的工作。再次,优雅被抛在脑后。
There are two callbacks defined by JAXB: beforeUnmarshal and afterUnmarshal. By implementing afterUnmarshal to clean up the list, I was able to achieve the desired result.
JAXB 定义了两个回调:beforeUnmarshal 和 afterUnmarshal。通过执行 afterUnmarshal 来清理列表,我能够达到预期的结果。
class Course
{
@XmlElement (name = "book")
List<Book> requiredBooks = new ArrayList<Book>();
...
void afterUnmarshal(Unmarshaller aUnmarshaller, Object aParent)
{
if (requiredBooks != null)
{
Iterator<Book> iterator = requiredBooks.iterator();
while (iterator.hasNext())
{
Book book = iterator.next();
if (StringUtils.isEmpty(book.getTitle()))
{
// a book without title is considered invalid
iterator.remove();
}
}
}
}
}
While this works, by biggest issue with it is the absence of an interface for implementing afterUnmarshal. It's looked up by JAXB using reflection, but I think it would've been convenient (and would reduce debugging/maintenance) if JAXB simply supplied interfaces and/or absract implementations. As it is, what if the signature of afterUnmarshal changes in the future? This code will just mysteriously stop working as it's supposed to.
虽然这有效,但最大的问题是缺少用于实现 afterUnmarshal 的接口。JAXB 使用反射查找它,但我认为如果 JAXB 只是提供接口和/或抽象实现,它会很方便(并且会减少调试/维护)。事实上,如果 afterUnmarshal 的签名在未来发生变化怎么办?这段代码会莫名其妙地停止工作。
回答by bdoughan
In EclipseLink JAXB (MOXy)we have the concept of a null policy. This null policy gives you some flexibility on how to represent null.
在EclipseLink JAXB (MOXy) 中,我们有空策略的概念。此 null 策略在如何表示 null 方面为您提供了一些灵活性。
You could modify your class to look like the following:
您可以将类修改为如下所示:
import org.eclipse.persistence.oxm.annotations.XmlMarshalNullRepresentation;
import org.eclipse.persistence.oxm.annotations.XmlNullPolicy;
@XmlRootElement
class Course {
@XmlElement (name = "book")
@XmlNullPolicy(
nullRepresentationForXml = XmlMarshalNullRepresentation.EMPTY_NODE,
emptyNodeRepresentsNull=true)
List<Book> requiredBooks = new ArrayList<Book>();
}
This informs MOXy JAXB that for this property you wish to treat empty elements as null for this property.
这会通知 MOXy JAXB,对于此属性,您希望将此属性的空元素视为空元素。
For this XML:
对于此 XML:
<course>
<book/>
<book>
<title>Hello World</title>
</book>
<book/>
</course>
The requiredBooksproperty would unmarshal as: null, aBook, null.
该requiredBooks属性将解组为:空,ABOOK,空。
There is currently a bug that is preventing this from working that we are addressing now. You can track the progress on this issue here:
目前有一个错误阻止了我们现在正在解决的问题。您可以在此处跟踪此问题的进度:
回答by torngat
Well, I found one way to do it, although I don't find it super elegant.
好吧,我找到了一种方法来做到这一点,尽管我觉得它不是超级优雅。
Edit:Actually, the solution below doesn'twork properly. It actually causes no entries to be added to the list, regardless of them being an empty element or fully formed with data.
编辑:实际上,下面的解决方案无法正常工作。它实际上不会导致任何条目添加到列表中,无论它们是空元素还是完全由数据构成。
Maybe I've done it wrong, so any comments are welcome.
也许我做错了,所以欢迎任何评论。
I created an adapter to handle the conversion and informed JAXB using an annotation:
我创建了一个适配器来处理转换并使用注释通知 JAXB:
class Course {
@XmlElement (name = "book")
@XmlJavaTypeAdapter(BookListAdapter.class)
List<Book> requiredBooks = new ArrayList<Book>();
and then defined my adapter:
然后定义我的适配器:
static class BookListAdapter extends XmlAdapter<Book[], List<Book>>
{
public Book[] marshal(List<Book> aBookList)
{
if (aBookList!= null)
{
return aBookList.toArray(new Book[aBookList.size()]);
}
return null;
}
public List<Book> unmarshal(Book[] aBookArray)
{
List<Book> bookList = new ArrayList<Book>();
if (aBookArray != null)
{
for (Book book : aBookArray)
{
if (StringUtils.isNotEmpty(book.getTitle()))
{
bookList.add(book);
}
}
}
return bookList;
}
}
It works as I want it to, but, as I said earlier, I'm not convinced of its elegance.
Edit:As described above, this doesn't work but probably due to some error on my part.
它按我的意愿工作,但是,正如我之前所说,我不相信它的优雅。
编辑:如上所述,这不起作用,但可能是由于我的一些错误。
回答by Adeel Ansari
Try this,
试试这个,
class Course {
@XmlElement (name="book", required=false)
List<Book> requiredBooks = new ArrayList<Book>();
回答by matt burns
I did a similar cludge:
我做了一个类似的cludge:
public List<Photo> getPhotos() {
removeUninitializedPhotos();
return photos;
}
private void removeUninitializedPhotos() {
List<Photo> photosToRemove = new ArrayList<Photo>();
for (Photo photo : photos) {
if (photo.getId() == null) {
photosToRemove.add(photo);
}
}
photos.removeAll(photosToRemove);
}
But I found it so horrible that I just ended up switching to Gson;)
但我发现它太可怕了,以至于我最终切换到Gson;)
回答by karelok
I had similar problem - I've need to handle an ArrayList containing interfaces. I've just created an adapter for interface (MyInterfaceAdapter) and annotated the List:
我有类似的问题 - 我需要处理一个包含接口的 ArrayList。我刚刚为接口(MyInterfaceAdapter)创建了一个适配器并注释了列表:
@XmlElementWrapper(name="myInterface")
@XmlJavaTypeAdapter(value=MyInterfaceAdapter.class)
List<MyInterface> list = new ArryList<MyInterface>;
My adapter looks like this:
我的适配器看起来像这样:
public class MyInterfaceAdapter extends XmlAdapter<MyType, MyInterface> {
@Override
public Sensor unmarshal(MyType v) throws Exception {
return (MyInterface) v;
}
@Override
public AbstractSensor marshal(MyInterface v) throws Exception {
if(v == null) //In the case of empty list
{
return null;
} else
{ .....return new ... }
}
}
After unmarshalling I've got an initialized ArrayList (even empty).
解组后,我得到了一个初始化的 ArrayList(甚至是空的)。
I think in the problem asked above one can add additional conditions and return null in the case of empty book. On the other hand I also dislike returning null.
我认为在上面提出的问题中,可以添加附加条件并在空书的情况下返回 null。另一方面,我也不喜欢返回 null。