Search code examples
javaxmlxml-parsingjaxbxstream

how to skip xml elements while loading into pojo (in xstream/jaxb)


I have a very big and complex xml and i want to load only selected fields into my object. I have tried with xstream but what i understood is my xml structure must be similar to my pojo. I am providing sample i/p and o/p for better understanding

<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>

Pojo will be

    class Note{
     long noteId;
     String body;

    //getters and setters

   }

Question is how to skip xml elements while loading it into pojo?


Solution

  • You can do it with Jackson XML librairy. Jackson is maybe more famous for JSON serialising/deserialising but it has a XML extension.

    If your XML file is big, look at the above link to see how you can partially/incrementally read it in order to get the best performances.

    Anyway following is an example on how to read only a subset of properties using the @JsonIgnore annotation on the fields you don't want deserialised:

    @JacksonXmlRootElement(localName = "jokes")
    class Jokes {
    
        @JacksonXmlProperty(localName = "joke")
        @JacksonXmlElementWrapper(useWrapping = false)
        private Joke[] jokes;
    
        // getter, setter omitted
    }
    
    class Joke {
        @JacksonXmlProperty(isAttribute = true)
        long id;
    
        @JacksonXmlProperty(localName = "title")
        String title;
    
        @JsonIgnore
        String author;
    
        @JacksonXmlProperty(localName = "like")
        long like;
    
        @JsonIgnore
        String content;
    
        public String toString() {
            return Arrays.stream(new Object[] {id, title, author, like, content})
                    .map(o -> o!=null ? o.toString() : "EMPTY")
                    .collect(Collectors.joining(", ", "[", "]"));
        }
    
        // getters, setters omitted
    }
    

    With a sample file:

    <jokes>
        <joke id="123">
            <title>C'est l'histoire d'un mec</title>
            <author>Coluche</author>
            <content>Blah blah blah</content>
            <like>4509</like>
        </joke>
        <joke id="777">
            <title>Si j'ai bien tout lu freud</title>
            <author>Coluche</author>
            <content>Blah blah blah</content>
            <like>345</like>
        </joke>
    </jokes>
    

    And the main()

    public class XmlJokeReader {
    
        public static void main(String[] args) throws JsonProcessingException, IOException {
            XmlMapper mapper = new XmlMapper();
    
            Jokes jokesDoc = mapper.readValue(new File("./data/jokes.xml"), Jokes.class);
            Arrays.stream(jokesDoc.getJokes()).forEach(j -> System.out.println(j.toString()));
        }
    }
    

    The output is (note the EMPTY fields):

    [123, C'est l'histoire d'un mec, EMPTY, 4509, EMPTY]
    [777, Si j'ai bien tout lu freud, EMPTY, 345, EMPTY]

    You can also create a pojo that contains only the fields you need - then not using @JsonIgnore. For that the XmlMapper has to be informed to ignore unknown XML properties.

        XmlMapper mapper = new XmlMapper();
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    

    EDIT

    The full example for the case where you want a pojo with only a few fields:

    Let say we have a pojo with only id and title:

    class Joke {
        @JacksonXmlProperty(isAttribute = true)
        long id;
    
        @JacksonXmlProperty(localName = "title")
        String title;
        
        public String toString() {
            return new StringBuffer().append('[')
                    .append(id).append(',')
                    .append(title).append(']').toString();
        }
        // getters setters 
    }
    

    Executing the following main() with the xml file described above:

    public class XmlJokeReader {

        public static void main(String[] args) throws JsonProcessingException, IOException {
            XmlMapper mapper = new XmlMapper();
            mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    
            Jokes jokesDoc = mapper.readValue(new File("./data/jokes.xml"), Jokes.class);
            for (Joke joke : jokesDoc.getJokes()) {
               System.out.println(joke.toString());
            }
        }
    }
    

    Will give:

    [123,C'est l'histoire d'un mec]
    [777,Si j'ai bien tout lu freud]