【发布时间】:2011-03-21 12:10:04
【问题描述】:
我有一个大型 XML 文档,我想将其转换为 Java bean。它有很多标签和属性,但我只对其中的一小部分感兴趣。不幸的是,XStream 似乎强迫您在该 bean 中为该 XML 中可能存在的每个标记声明一个属性。有没有办法解决这个问题?
【问题讨论】:
我有一个大型 XML 文档,我想将其转换为 Java bean。它有很多标签和属性,但我只对其中的一小部分感兴趣。不幸的是,XStream 似乎强迫您在该 bean 中为该 XML 中可能存在的每个标记声明一个属性。有没有办法解决这个问题?
【问题讨论】:
XStream 1.4.5 让您轻松处理未知标签。使用ignoreUnknownElements() 标记尚未实现或已被删除并且您正在处理旧xml。您还可以指定要忽略的特定标签。
【讨论】:
由于 XStream 1.4.5 在编组器声明期间使用 ignoreEnknownElements() 方法就足够了:
XStreamMarshaller marshaller = new XStreamMarshaller();
marshaller.getXStream().ignoreUnknownElements();
...
忽略不必要的元素。
【讨论】:
在您的 XStream 实例中使用 ignoreUnknownElements() 方法:
XStream xstream = new XStream();
xstream.ignoreUnknownElements();
【讨论】:
如下所示初始化XStream以忽略未在您的bean中定义的字段。
XStream xstream = new XStream() {
@Override
protected MapperWrapper wrapMapper(MapperWrapper next) {
return new MapperWrapper(next) {
@Override
public boolean shouldSerializeMember(Class definedIn, String fieldName) {
if (definedIn == Object.class) {
return false;
}
return super.shouldSerializeMember(definedIn, fieldName);
}
};
}
};
【讨论】:
我今天一直在努力解决这个问题,我发现使用 return this.realClass(fieldName) != null; 并不是(总是)一个可行的解决方案,但实际上 XStream 有一种方法跳过未映射的标签并同时使用隐式集合。
为什么realClass(fieldName) 不起作用
实际上使用技巧
try {
return this.realClass(fieldName) != null;
} catch (Throwable t) {
return false;
}
有效。它所做的是尝试通过标签名称猜测类型,看看它是否成功,如果没有 - 返回 false。所以它会完美地跳过像
这样的标签<someUnknownTag>someContent</someUnknownTag>
但它只会在某些“不需要”标签碰巧有一个有意义的名称时起作用 (!)等于 null 并且该标签不会是您的任何 ImplicitCollection 的成员。在那种情况下,知道可以定义 xml 元素的类并且在用户类型 XStream 中没有映射这样的字段将决定“也许这个元素来自某个隐式集合”。如果您的班级既没有这样的集合也没有字段,它很快就会失败。在我的情况下,有问题的 xml 是这样的:
<url>http://somewhere.com</url>
当然,我的班级既没有Url url; 也没有@XStreamImplicit List<Url> url。拥有这样一个 XML 并使用“realClass”事物的结果如下:
com.thoughtworks.xstream.converters.ConversionException: Element url of type java.net.URL is not defined as field in type org.sample.xstream.SomeBean
正确的方式
正确的方法是从shouldSerializeMember 返回普通的false,以防definedIn == Object.class(不使用realClass(fieldName) 的东西)。
但仅使用return false 是不够的。在这种形式下,它将导致 XStream 将隐式集合留空。
这里的技巧是确保使用@XStreamImplicit(itemFieldName = "something") 而不是只使用不带参数的@XStreamImplicit,即使在标签名称和集合的通用参数类型具有相同名称的情况下也是如此。
所以正确的代码应该是这样的:
xstream = new XStream() {
@Override
protected MapperWrapper wrapMapper(MapperWrapper next) {
return new MapperWrapper(next) {
@Override
public boolean shouldSerializeMember(Class definedIn, String fieldName) {
if (definedIn == Object.class) {
//This is not compatible with implicit collections where item name is not defined
return false;
} else {
return super.shouldSerializeMember(definedIn, fieldName);
}
}
};
}
};
xsteam.processAnnotations(SomeRootEntry.class);
并且您需要确保在您的类中,您的隐式集合标记如下:
@XStreamImplicit(itemFieldName = "something")
private List <Something> somethingList;
注意itemFieldName 是显式指定的,即使 List 的泛型类型参数具有相同的名称。这很关键。
在这种情况下,遇到<something> 标签时,XStream 甚至不会使用该字段名访问您的shouldSerializeMember。它只会提前知道元素来自隐式集合。
当它将访问你的方法是在再次遇到<url>http://somewhere.com</url>时。但是在这里我们是安全的,因为我们只是返回false。
为我工作!试试看吧。
【讨论】: