java - 如何使用 LinkedHashMap 解析包含多个标签的 xml 文件?
问题描述
我想构建一个解析器来XML
解析Java
. 正如您在下面的代码中看到的那样,我使用 aLinkedHashMap
来访问所需的值First
,Middle
并且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();
}
由于我的代码非常混乱,您知道如何使其更小吗?
我会很高兴得到一些帮助。
谢谢!
解决方案
通常列表不容易处理,当我们不使用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'}]}}
另请参阅:解析时如何忽略外包装?
推荐阅读
- html - 如何在文本周围创建 CSS 边框角
- laravel - 现在的碳。忽略第二个参数。
- python-3.x - 在python中修复不一致的csv文件的最佳方法
- python - 获取列表中相同值的序列并计算序列中的元素
- c++ - 如何在多阶段 Docker 构建的阶段之间复制库文件,同时保留符号链接?
- angular - ng-2 图表时刻 js 依赖
- streaming - GStreamer 多播 UDP 流在客户端中没有设置“sprop-parameter-sets”上限时无法工作
- twitter-bootstrap - 在 Bootstrap 4 中获取具有相同宽度的填充列
- java - java在Tomcat中执行脚本
- sql-server - MS-SQL:连接虚拟主机后如何过滤数据库