У меня есть большой XML-документ, который я хочу преобразовать в Java bean. В нем много тегов и атрибутов, но меня интересует только несколько из них. Безгранично кажется, что XStream заставляет вас объявить свойство в bean для каждого тега, который может когда-либо быть в этом XML. Есть ли способ обойти это?
Как заставить XStream пропускать немаркированные теги при разборе XML?
Ответ 1
Инициализируйте 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);
}
};
}
};
Ответ 2
XStream 1.4.5 упрощает работу с неизвестными тегами. Используйте ignoreUnknownElements для тегов, которые еще не реализованы или были удалены, и вы имеете дело со старым xml http://x-stream.github.io/javadoc/com/thoughtworks/xstream/XStream.html#ignoreUnknownElements%28%29. Вы также можете указать, какой конкретный тег вы хотите игнорировать.
Ответ 3
Так как XStream 1.4.5 выдает объявление маршаллера, достаточно использовать метод ignoreEnknownElements():
XStreamMarshaller marshaller = new XStreamMarshaller();
marshaller.getXStream().ignoreUnknownElements();
...
игнорировать ненужные элементы.
Ответ 4
Сегодня я столкнулся с этой проблемой, и выяснилось, что использование 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
Правильный путь
Правильный путь будет возвращать plain false
из shouldSerializeMember
в случае, если definedIn == Object.class
(не используя realClass(fieldName)
stuff).
Но просто использовать только 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
явно задан, даже если параметр типа Generic типа имеет то же имя. Это важно.
В этом случае при столкновении с тегом <something>
XStream даже не будет посещать ваш shouldSerializeMember
с этим полемName. Он просто будет знать заранее, что элемент из неявных коллекций.
Когда он посетит ваш метод, снова встретите <url>http://somewhere.com</url>
. Но здесь мы в безопасности, так как мы просто возвращаем false
.
Работает для меня! Попробуйте.