How to make XStream skip unmapped tags when parsing XML?

柔情痞子 提交于 2019-12-03 04:56:20

问题


I have a large XML document that I want to convert to a Java bean. It has a lot of tags and attributes, but I'm interested only in a handful of those. Unfurtounately, it seems that XStream forces you to declare a property in that bean for each and every tag that may ever be in that XML. Is there a way around this?


回答1:


Initialize XStream as shown below to ignore fields that are not defined in your 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 makes you simple to deal with unknown tags. Use ignoreUnknownElements() for tags which are not implemented yet or has been removed and you are dealing with old xml. You can also specify which particular tag you would like to ignore.





回答3:


Since XStream 1.4.5 durring marshaller declaration it's enough to use ignoreEnknownElements() method:

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

to ignore unnecessary elements.




回答4:


I've been working my way around this problem today and what I've found out is that using return this.realClass(fieldName) != null; is not (always) a working solution however actually there is a way for XStream to skip unmapped tags AND work with implicit collections at the same time.

Why realClass(fieldName) thing won't work

In fact trick with using

try {
    return this.realClass(fieldName) != null;
} catch (Throwable t) {
    return false;
}

works. What it does is attempt to guess the type by the tag name, see whether it succeeded and if not - returns false. So it'll perfectly skip tags like

<someUnknownTag>someContent</someUnknownTag>

BUT it will work only up to the moment (!) when somehow some "not needed" tag will happen to have a meaningful name for which realClass(fieldName) will be actually able to return something not equal to null and that tag won't be a member of any ImplicitCollection of yours. In that case knowing that the class for an xml element could be defined and there is no such field mapped in users type XStream will decide that "maybe this element is from some implicit collection". And it will fail very soon if there is neither such collection nor a field in your class. In my case the problematic piece of xml was like this:

<url>http://somewhere.com</url>

and, of course, there was neither Url url; nor @XStreamImplicit List<Url> url on my class. The result of having such an XML and using "realClass" thing is as follows:

com.thoughtworks.xstream.converters.ConversionException: Element url of type java.net.URL is not defined as field in type org.sample.xstream.SomeBean

The right way

The right way would be returning plain false from shouldSerializeMember in case when definedIn == Object.class (not using realClass(fieldName) stuff).

But just using return false alone is not enough. I this form it will cause XStream to leave implicit collections empty.

The trick here is to make sure that one uses @XStreamImplicit(itemFieldName = "something") instead of just using @XStreamImplicit without parameters even in the cases when tag name and collection's generic param type have the same name.

So the right code will look like this:

    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);

And you need to be sure that in your classes your implicit collections are marked like this:

@XStreamImplicit(itemFieldName = "something")
private List <Something> somethingList;

Notice that itemFieldName is explicitly specified even though List's generic type parameter has the same name. This is crucial.

In this case upon encountering <something> tag XStream won't even visit your shouldSerializeMember with that fieldName. It will just know in advance that the element is from implicit collections.

When it will visit your method is upon encountering <url>http://somewhere.com</url> again. But here we're safe since we just return false.

Works for me! Give it a try.




回答5:


Use ignoreUnknownElements() method in your XStream instance:

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


来源:https://stackoverflow.com/questions/5377380/how-to-make-xstream-skip-unmapped-tags-when-parsing-xml

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!