首页 > 解决方案 > 如何使用 LinkedHashMap 解析包含多个标签的 xml 文件?

问题描述

我想构建一个解析器来XML解析Java. 正如您在下面的代码中看到的那样,我使用 aLinkedHashMap来访问所需的值FirstMiddle并且Last.

我的问题是我的NameList标签包含多个Person标签,LinkedHashMap到目前为止Person,在我的示例(输出)中只给了我最后一个:

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

如何使用我的方法访问其他两个(Karl Abc、Thomas Def)LinkedHashMap

这是我的XML文件:

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

这是我的代码:

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

我的助手类:

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

由于我的代码非常混乱,您知道如何使其更小吗?

我会很高兴得到一些帮助。

谢谢!

标签: javaxmlparsingjacksonlinkedhashmap

解决方案


通常列表不容易处理,当我们不使用POJO结构时,我们需要编写自定义反序列化器,它将使用Streaming API来读取内部列表对象。您可以在下面找到POJO带有自定义反序列化器的简单模型Author

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
}

上面的代码打印:

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

您可以打印整个Source实例:Stream.of(sources).forEach(System.out::println);并且您应该看到:

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'}]}}

另请参阅:解析时如何忽略外包装?


推荐阅读