Сегодня я обошел эту проблему и обнаружил, что с использованием return this.realClass(fieldName) != null;
не является (всегда) рабочим решением , однако на самом деле для XStream есть способ пропускайте не отображенные теги и одновременно работайте с неявными коллекциями.
Почему realClass(fieldName)
вещь не работает
На самом деле трюк с использованием
try {
return this.realClass(fieldName) != null;
} catch (Throwable t) {
return false;
}
работает. Что он делает, это пытается угадать тип по имени тега, посмотреть, успешно ли он, а если нет - возвращает false. Так что он отлично пропустит теги типа
<someUnknownTag>someContent</someUnknownTag>
НО это будет работать только до момента (!) , когда каким-то образом «ненужный» тег окажется с осмысленным именем, для которого realClass(fieldName)
действительно сможет вернуть что-то не равное на 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
Правильный путь
Правильный путь будет возвращать обычный false
из shouldSerializeMember
в случае, если 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
с этим fieldName. Он просто заранее знает, что этот элемент из неявных коллекций.
Когда он будет посетить ваш метод, при встрече с <url>http://somewhere.com</url>
снова. Но здесь мы в безопасности, так как мы просто возвращаем false
.
У меня работает! Попробуйте.