Как заставить XStream пропускать несопоставленные теги при разборе XML? - PullRequest
20 голосов
/ 21 марта 2011

У меня большой XML-документ, который я хочу преобразовать в Java-бин. У него много тегов и атрибутов, но меня интересует лишь несколько из них. К сожалению, кажется, что XStream заставляет вас объявлять свойство в этом bean-компоненте для каждого тега, который может быть в этом XML. Есть ли способ обойти это?

Ответы [ 5 ]

18 голосов
/ 15 октября 2013

XStream 1.4.5 упрощает работу с неизвестными тегами. Используйте ignoreUnknownElements() для тегов, которые еще не реализованы или были удалены, и вы имеете дело со старым XML. Вы также можете указать, какой именно тег вы хотите игнорировать.


17 голосов
/ 22 апреля 2011

Инициализируйте XStream, как показано ниже, чтобы игнорировать поля, которые не определены в вашем бине.

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);
            }
        };
    }
};
9 голосов
/ 17 июля 2014

Начиная с XStream 1.4.5 во время объявления маршаллера, достаточно использовать метод ignoreEnknownElements ():

XStreamMarshaller marshaller = new XStreamMarshaller();
marshaller.getXStream().ignoreUnknownElements();
...

, чтобы игнорировать ненужные элементы.

7 голосов
/ 26 июля 2013

Сегодня я обошел эту проблему и обнаружил, что с использованием 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.

У меня работает! Попробуйте.

2 голосов
/ 22 апреля 2018

Используйте ignoreUnknownElements () метод в вашем экземпляре XStream:

XStream xstream = new XStream();
xstream.ignoreUnknownElements();
...