How to parse xml file containing multiple tags using LinkedHashMap?

|▌冷眼眸甩不掉的悲伤 提交于 2019-12-13 03:31:41

问题


I want to build a parser to parse an XML file in Java. As you can see in my code below I am using a LinkedHashMap to access the desired values First, Middle and Last.

My problem is that I have NameList tags containing multiple Person tags and the LinkedHashMap so far gives me only the last Person, in my example (output):

given: Ghi family: Tom given: Jkl family: Mary

How can I access the other two (Karl Abc, Thomas Def) using my approach with LinkedHashMap?

This is my XML file:

<Sources>
<Source>
    <Year>2019</Year>
</Source>
<Source>
    <Title>Blablabla</Title>
    <Author>
        <BookAuthor>
            <NameList>
                <Person>
                    <Last>Karl</Last>
                    <First>Abc</First>
                </Person>
                <Person>
                    <Last>Thomas</Last>
                    <First>Def</First>
                </Person>
                <Person>
                    <Last>Tom</Last>
                    <First>Ghi</First>
                </Person>
            </NameList>
        </BookAuthor>
    </Author>
</Source>
<Source>
    <Author>
        <Editor>
            <NameList>
                <Person>
                    <Last>Mary</Last>
                    <First>Jkl</First>
                </Person>
            </NameList>
        </Editor>
    </Author>
</Source>

This is my code:

private static void XmlFileParser() throws IOException {

    InputStream xmlFile = Publication.class.getClassLoader().getResourceAsStream("test.xml");
    ObjectMapper mapper = new XmlMapper();

    // Configure
    mapper
            .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

    try {

        Object[] deserializedData = mapper.readValue(xmlFile, Object[].class);

        for (Object element : deserializedData) {

            if (element instanceof LinkedHashMap) {
                LinkedHashMap<String, Object> el = (LinkedHashMap<String, Object>) element;

                if ((el.get("Author")) == null) {
                    continue;
                } else {

                    // Last -> family
                    // First, Middle -> given

                    if (((LinkedHashMap) el.get("Author")).get("Author") instanceof LinkedHashMap && ((((LinkedHashMap) ((LinkedHashMap) el
                            .get("Author")).get("Author")).get("NameList")) != null)) {
                        Object first = ((LinkedHashMap) ((LinkedHashMap) ((LinkedHashMap) ((LinkedHashMap) el.get(
                                "Author")).get("Author")).get("NameList")).get("Person")).get("First");
                        Object middle = ((LinkedHashMap) ((LinkedHashMap) ((LinkedHashMap) ((LinkedHashMap) el.get(
                                "Author")).get("Author")).get("NameList")).get("Person")).get("Middle");
                        if (first != null || middle != null) {
                            System.out.println("given: " + evaluateGiven(first, middle));
                        }

                        Object family = ((LinkedHashMap) ((LinkedHashMap) ((LinkedHashMap) ((LinkedHashMap) el.get(
                                "Author")).get("Author")).get("NameList")).get("Person")).get("Last");
                        System.out.println("family: " + family);
                    } else if (((LinkedHashMap) el.get("Author")).get("Editor") instanceof LinkedHashMap && ((((LinkedHashMap) ((LinkedHashMap) el
                            .get("Author")).get("Editor")).get("NameList")) != null)) {
                        Object first = ((LinkedHashMap) ((LinkedHashMap) ((LinkedHashMap) ((LinkedHashMap) el.get(
                                "Author")).get("Editor")).get("NameList")).get("Person")).get("First");
                        Object middle = ((LinkedHashMap) ((LinkedHashMap) ((LinkedHashMap) ((LinkedHashMap) el.get(
                                "Author")).get("Editor")).get("NameList")).get("Person")).get("Middle");
                        if (first != null || middle != null) {
                            System.out.println("given: " + evaluateGiven(first, middle));
                        }

                        Object family = ((LinkedHashMap) ((LinkedHashMap) ((LinkedHashMap) ((LinkedHashMap) el.get(
                                "Author")).get("Editor")).get("NameList")).get("Person")).get("Last");
                        System.out.println("family: " + family);
                    } 
                }
            }
        }

    } catch (IOException e) {
        e.printStackTrace();
    }
}

My helper class:

private static String evaluateGiven(Object first, Object middle) {
    if (first == null) {
        first = "";
    } else if (middle == null) {
        middle = "";
    }
    return first.toString() + " " + middle.toString();
}

Since my code is very blown up, do you know how to make it smaller?

I would be very happy about some help.

Thank you!


回答1:


Generally lists are not handled easily and when we do not use POJO structure we need to write custom deserialiser which will use Streaming API to read inner list objects. Below you can find simple POJO model with custom deserialiser for Author class:

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;

public class XmlMapperApp {

    public static void main(String[] args) throws Exception {
        File xmlFile = new File("./resource/test.xml").getAbsoluteFile();

        XmlMapper xmlMapper = new XmlMapper();
        xmlMapper.setDefaultUseWrapper(false);
        xmlMapper.setPropertyNamingStrategy(PropertyNamingStrategy.UPPER_CAMEL_CASE);

        Source[] sources = xmlMapper.readValue(xmlFile, Source[].class);
        Stream.of(sources)
                .filter(s -> Objects.nonNull(s.getAuthor()))
                .map(s -> s.getAuthor().getPersons())
                .filter(a -> !a.isEmpty())
                .forEach(System.out::println);
    }
}

class AuthorJsonDeserializer extends JsonDeserializer<Author> {
    @Override
    public Author deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        p.nextToken(); // Skip START_OBJECT
        Author author = new Author();
        author.setType(p.getText());
        author.setPersons(new ArrayList<>());

        JsonToken token;
        while ((token = p.currentToken()) != JsonToken.END_OBJECT) {
            if (token == JsonToken.FIELD_NAME) {
                String name = p.getText();
                if ("Person".equals(name)) {
                    p.nextToken();
                    author.getPersons().add(p.readValueAs(Person.class));
                }
            }
            p.nextToken();
        }
        p.nextToken(); // Skip END_OBJECT
        p.nextToken(); // Skip END_OBJECT

        return author;
    }
}

class Source {

    private int year;
    private String title;
    private Author author;

    // getters, setters, toString
}

@JsonDeserialize(using = AuthorJsonDeserializer.class)
class Author {

    private String type;
    private List<Person> persons;

    // getters, setters, toString
}

class Person {

    private String last;
    private String first;

    // getters, setters, toString
}

Above code prints:

[Person{last='Karl', first='Abc'}, Person{last='Thomas', first='Def'}, Person{last='Tom', first='Ghi'}]
[Person{last='Mary', first='Jkl'}]

You can print whole Source instance: Stream.of(sources).forEach(System.out::println); and you should see:

Source{year=2019, title='null', author=null}
Source{year=0, title='Blablabla', author=Author{type='BookAuthor', persons=[Person{last='Karl', first='Abc'}, Person{last='Thomas', first='Def'}, Person{last='Tom', first='Ghi'}]}}
Source{year=0, title='null', author=Author{type='Editor', persons=[Person{last='Mary', first='Jkl'}]}}

See also: how to ignore outer wrappers when parsing?



来源:https://stackoverflow.com/questions/57297149/how-to-parse-xml-file-containing-multiple-tags-using-linkedhashmap

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