<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.21</version>
</dependency>
基本使用
实体类转 xml
实体类对象:
@Data
@Builder
public class Person {
private String firstname;
private String lastname;
private PhoneNumber phone;
private PhoneNumber phone2;
}
@Data
@Builder
public class PhoneNumber {
private int code;
private String number;
}
demo 类:
PhoneNumber phone = PhoneNumber.builder()
.number("13797458888")
.code(12345)
.build();
PhoneNumber phone2 = PhoneNumber.builder()
.number("17855555555")
.code(54231)
.build();
Person person = Person.builder().firstname("tom")
.lastname("cat")
.phone(phone)
.phone2(phone2)
.build();
XStream xStream = new XStream();
String xmlStr = xStream.toXML(person);
System.out.println(xmlStr);
结果:
可以发现,属性的名称是我们期望的,但是根标签的类名映射为了全路径名。
<cn.bugstack.xfg.dev.tech.test.Person>
<firstname>tom</firstname>
<lastname>cat</lastname>
<phone>
<code>12345</code>
<number>13797458888</number>
</phone>
<phone2>
<code>54231</code>
<number>17855555555</number>
</phone2>
</cn.bugstack.xfg.dev.tech.test.Person>
我们加上 xStream.alias("person",Perzson.class)
这个方法可以指定类映射的标签名。
再次运行,查看结果
<person>
<firstname>tom</firstname>
<lastname>cat</lastname>
<phone>
<code>12345</code>
<number>13797458888</number>
</phone>
<phone2>
<code>54231</code>
<number>17855555555</number>
</phone2>
</person>
另外我试了几下对 phoneNumber 也设置映射名,但是结果没有生效,看来 alias 设置的名字只对根标签有效。
xml 转实体
public void t2(){
String xml1 = "<person>" +
" <firstname>tom</firstname>" +
" <lastname>cat</lastname>" +
" <phone>" +
" <code>12345</code>" +
" <number>13797458888</number>" +
" </phone>" +
" <phone2>" +
" <code>54231</code>" +
" <number>17855555555</number>" +
" </phone2>" +
"</person>";
XStream xStream = new XStream();
xStream.alias("person",Person.class);
Person person = (Person) xStream.fromXML(xml1);
System.out.println(person);
}
输出:
Person(firstname=tom, lastname=cat, phone=PhoneNumber(code=12345, number=13797458888), phone2=PhoneNumber(code=54231, number=17855555555))
使用 alias 设置类名或属性的标签名
alias 设置类名对应的标签名上面已经介绍过了,接下来看设置属性对应的标签名如何编写。
方法:public void aliasField(String alias, Class definedIn, String fieldName)
,alias 为要取的名字,definedIn 为这个属性属于的类,fieldName 为属性名。
public void t1() {
PhoneNumber phone = PhoneNumber.builder()
.number("13797458888")
.code(12345)
.build();
PhoneNumber phone2 = PhoneNumber.builder()
.number("17855555555")
.code(54231)
.build();
Person person = Person.builder()
.firstname("tom")
.lastname("cat")
.phone(phone)
.phone2(phone2)
.build();
XStream xStream = new XStream();
xStream.alias("person",Person.class);
xStream.aliasField("firstfirstname",Person.class,"firstname");
xStream.aliasField("lastlastname",Person.class,"lastname");
String xmlStr = xStream.toXML(person);
System.out.println(xmlStr);
}
输出结果:
<person>
<firstfirstname>tom</firstfirstname>
<lastlastname>cat</lastlastname>
<phone>
<code>12345</code>
<number>13797458888</number>
</phone>
<phone2>
<code>54231</code>
<number>17855555555</number>
</phone2>
</person>
处理集合
@Data
@AllArgsConstructor
public class Blog {
private Author writer;
private List entries = new ArrayList();
public Blog(Author writer) {
this.writer = writer;
}
public void add(Entry entry) {
entries.add(entry);
}
public List getContent() {
return entries;
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Author {
private String name;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Entry {
private String title;
private String description;
}
测试将对象转为 xml:
public void t1() {
Blog teamBlog = new Blog(new Author("张三"));
teamBlog.add(new Entry("article1", "My first blog entry."));
teamBlog.add(new Entry("article2",
"Today we have developed a nice alias tutorial."));
XStream xstream = new XStream();
System.out.println(xstream.toXML(teamBlog));
}
输出:
<cn.bugstack.xfg.dev.tech.test.list.Blog>
<writer>
<name>张三</name>
</writer>
<entries>
<cn.bugstack.xfg.dev.tech.test.list.Entry>
<title>article1</title>
<description>My first blog entry.</description>
</cn.bugstack.xfg.dev.tech.test.list.Entry>
<cn.bugstack.xfg.dev.tech.test.list.Entry>
<title>article2</title>
<description>Today we have developed a nice alias tutorial.</description>
</cn.bugstack.xfg.dev.tech.test.list.Entry>
</entries>
</cn.bugstack.xfg.dev.tech.test.list.Blog>
发现集合的名字是 entries 没问题,但是里面的类是全路径名,加上 alias
xstream.alias("blog", Blog.class);
xstream.alias("entry", Entry.class);
输出:
<blog>
<writer>
<name>张三</name>
</writer>
<entries>
<entry>
<title>article1</title>
<description>My first blog entry.</description>
</entry>
<entry>
<title>article2</title>
<description>Today we have developed a nice alias tutorial.</description>
</entry>
</entries>
</blog>
隐藏集合根标签
如果你不希望出现 entries 这种根标签,想让集合里的元素直接列出来,你可以使用
xstream.addImplicitCollection(Blog.class, "entries");
这个方法的优先级高于 alias。
<blog>
<writer>
<name>张三</name>
</writer>
<entry>
<title>article1</title>
<description>My first blog entry.</description>
</entry>
<entry>
<title>article2</title>
<description>Today we have developed a nice alias tutorial.</description>
</entry>
</blog>
字符串集合
@Data
@XStreamAlias("school")
public class School {
private List<String> students;
}
public void t3() {
School school = new School();
ArrayList<String> list = new ArrayList<>();
list.add("张三");
list.add("李四");
list.add("王五");
school.setStudents(list);
XStream xstream = new XStream();
xstream.processAnnotations(school.getClass());
System.out.println(xstream.toXML(school));
}
输出:
<school>
<students>
<string>张三</string>
<string>李四</string>
<string>王五</string>
</students>
</school>
可以看到,标签名是 string。
修改一下,@XStreamImplicit 会删除集合的根标签,itemFieldName 用于指定集合种元素的标签名
@Data
@XStreamAlias("school")
public class School {
@XStreamImplicit(itemFieldName = "student")
private List<String> students;
}
输出:
<school>
<student>张三</student>
<student>李四</student>
<student>王五</student>
</school>
转换器-对象属性转字符串
创建转换器,xml 转对象,对象转 xml 都可以使用这个转换器(实现 SingleValueConverter,用于对象转为单一字符串,和单一字符串转为对象)
public class AuthorConvertor implements SingleValueConverter {
@Override
public String toString(Object obj) {
Author author = (Author) obj;
return author.getName();
}
@Override
public Object fromString(String str) {
return new Author(str);
}
@Override
public boolean canConvert(Class type) {
return type.equals(Author.class);
}
}
public void t1() {
Blog teamBlog = new Blog(new Author("张三"));
teamBlog.add(new Entry("article1", "My first blog entry."));
teamBlog.add(new Entry("article2",
"Today we have developed a nice alias tutorial."));
XStream xstream = new XStream();
xstream.alias("blog", Blog.class);
xstream.alias("entry", Entry.class);
xstream.addImplicitCollection(Blog.class, "entries");
xstream.registerConverter(new AuthorConvertor());
System.out.println(xstream.toXML(teamBlog));
}
输出:
可以看到,writer 标签里直接显示名字,而没有再嵌套一个 name 元素。
<blog>
<writer>张三</writer>
<entry>
<title>article1</title>
<description>My first blog entry.</description>
</entry>
<entry>
<title>article2</title>
<description>Today we have developed a nice alias tutorial.</description>
</entry>
</blog>
元素转为标签里的属性
xstream.useAttributeFor(Blog.class, “writer”):代表设置 blog 里的 writer 为 blog 的属性。
public void t1() {
Blog teamBlog = new Blog(new Author("张三"));
teamBlog.add(new Entry("article1", "My first blog entry."));
teamBlog.add(new Entry("article2",
"Today we have developed a nice alias tutorial."));
XStream xstream = new XStream();
xstream.alias("blog", Blog.class);
xstream.alias("entry", Entry.class);
xstream.addImplicitCollection(Blog.class, "entries");
xstream.useAttributeFor(Blog.class, "writer");
xstream.registerConverter(new AuthorConvertor());
System.out.println(xstream.toXML(teamBlog));
}
输出:
<blog writer="张三">
<entry>
<title>article1</title>
<description>My first blog entry.</description>
</entry>
<entry>
<title>article2</title>
<description>Today we have developed a nice alias tutorial.</description>
</entry>
</blog>
注解
@XStreamAlias
对象:
@Data
@AllArgsConstructor
@XStreamAlias("博客")
public class Blog {
@XStreamAlias("作者")
private Author writer;
@XStreamAlias("文章")
private List entries = new ArrayList();
public Blog(Author writer) {
this.writer = writer;
}
public void add(Entry entry) {
entries.add(entry);
}
public List getContent() {
return entries;
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@XStreamAlias("作者")
public class Author {
@XStreamAlias("name")
private String name;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@XStreamAlias("具体文章")
public class Entry {
@XStreamAlias("标题")
private String title;
@XStreamAlias("描述")
private String description;
}
xstream.processAnnotations(Blog.class)
调用该方法让注解生效。
public void t2() {
Blog teamBlog = new Blog(new Author("张三"));
teamBlog.add(new Entry("article1", "My first blog entry."));
teamBlog.add(new Entry("article2",
"Today we have developed a nice alias tutorial."));
XStream xstream = new XStream();
xstream.addImplicitCollection(Blog.class, "entries");
xstream.useAttributeFor(Blog.class, "writer");
xstream.registerConverter(new AuthorConvertor());
xstream.processAnnotations(Blog.class);
xstream.processAnnotations(Author.class);
xstream.processAnnotations(Entry.class);
System.out.println(xstream.toXML(teamBlog));
}
@XStreamImplicit-删除集合根标签
((20241119221458-5fbc7h9 ‘字符串集合’))
@XStreamAsAttribute
元素转属性
@XStreamOmitField
忽略该属性
转换器
SingleValueConverter
@Data
@XStreamAlias("date")
public class DateInfo {
@XStreamAlias("日期")
@XStreamConverter(value = DateConvertor.class)
private LocalDateTime localDateTime;
@XStreamAlias("好日子")
@XStreamConverter(value = BooleanConverter.class, booleans = {false}, strings = {"好日子", "坏日子"})
private boolean isGoodDay;
}
转换器
public class DateConvertor implements SingleValueConverter {
@Override
public String toString(Object obj) {
LocalDateTime date = (LocalDateTime) obj;
return "今天的日期:"+date;
}
@Override
public Object fromString(String str) {
String dateStr = str.replace("今天的日期:", "");
LocalDateTime result = LocalDateTime.parse(dateStr);
return result;
}
@Override
public boolean canConvert(Class type) {
return LocalDateTime.class.equals(type);
}
}
public void t3(){
DateInfo dateInfo = new DateInfo();
LocalDateTime now = LocalDateTime.now();
dateInfo.setLocalDateTime(now);
dateInfo.setGoodDay(true);
XStream xStream = new XStream();
xStream.processAnnotations(DateInfo.class);
String xml = xStream.toXML(dateInfo);
System.out.println(xml);
DateInfo o = (DateInfo) xStream.fromXML(xml);
System.out.println(o);
}
<date>
<日期>今天的日期:2024-11-19T22:51:47.905</日期>
<好日子>好日子</好日子>
</date>
DateInfo(localDateTime=2024-11-19T22:51:47.905, isGoodDay=true)
Convertor
实现该转换器,可以自定义序列化与反序列化的过程。
@Data
@AllArgsConstructor
@XStreamAlias("博客")
public class Blog {
@XStreamAlias("作者")
private Author writer;
@XStreamAlias("文章")
@XStreamImplicit
private List<Entry> entries = new ArrayList();
public Blog(Author writer) {
this.writer = writer;
}
public void add(Entry entry) {
entries.add(entry);
}
public List getContent() {
return entries;
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@XStreamAlias("作者")
public class Author {
@XStreamAlias("name")
private String name;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@XStreamAlias("具体文章")
public class Entry {
@XStreamAlias("标题")
private String title;
@XStreamAlias("描述")
private String description;
}
序列化
public class MyConvertor implements Converter {
@Override
public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
Blog blog = (Blog) source;
writer.startNode("zuozhe");
writer.setValue(blog.getWriter().getName());
writer.endNode();
writer.startNode("wenzhangList");
List<Entry> entries = blog.getEntries();
for (Entry entry : entries) {
writer.startNode("wenzhang");
writer.startNode("biaoti");
writer.setValue(entry.getTitle());
writer.endNode();
writer.startNode("desc");
writer.setValue(entry.getDescription());
writer.endNode();
writer.endNode();
}
writer.endNode();
}
@Override
public boolean canConvert(Class type) {
return Blog.class.equals(type);
}
}
输出:
<博客>
<zuozhe>张三</zuozhe>
<wenzhangList>
<wenzhang>
<biaoti>article1</biaoti>
<desc>My first blog entry.</desc>
</wenzhang>
<wenzhang>
<biaoti>article2</biaoti>
<desc>Today we have developed a nice alias tutorial.</desc>
</wenzhang>
</wenzhangList>
</博客>
反序列化
String xmlContent = "<博客>" +
"<zuozhe>张三</zuozhe>" +
"<wenzhangList>" +
" <wenzhang>" +
" <biaoti>article1</biaoti>" +
" <desc>My first blog entry.</desc>" +
" </wenzhang>" +
" <wenzhang>" +
" <biaoti>article2</biaoti>" +
" <desc>Today we have developed a nice alias tutorial.</desc>" +
" </wenzhang>" +
"</wenzhangList>" +
"</博客>";
关键在于moveDown,moveUp,hasMoreChildren,reader是从根节点触发的。
reader.hasMoreChildren()如果存在没有peek过的元素则会返回true。
public class MyConvertor implements Converter {
@Override
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
Blog blog = new Blog();
while (reader.hasMoreChildren()) {
reader.moveDown();
String nodeName = reader.getNodeName();
switch (nodeName) {
case "zuozhe":
Author author = new Author(reader.getValue());
blog.setWriter(author);
break;
case "wenzhangList":
List<Entry> entries = new ArrayList<>();
while (reader.hasMoreChildren()) {
reader.moveDown();
if (reader.getNodeName().equals("wenzhang")) {
Entry entry = new Entry();
while (reader.hasMoreChildren()) {
reader.moveDown();
switch (reader.getNodeName()) {
case "biaoti":
entry.setTitle(reader.getValue());
break;
case "desc":
entry.setDescription(reader.getValue());
break;
}
reader.moveUp();
}
entries.add(entry);
}
reader.moveUp();
}
blog.setEntries(entries);
break;
}
reader.moveUp();
}
return blog;
}
@Override
public boolean canConvert(Class type) {
return Blog.class.equals(type);
}
}
public void t31() {
String xmlContent = "<博客>" +
"<zuozhe>张三</zuozhe>" +
"<wenzhangList>" +
" <wenzhang>" +
" <biaoti>article1</biaoti>" +
" <desc>My first blog entry.</desc>" +
" </wenzhang>" +
" <wenzhang>" +
" <biaoti>article2</biaoti>" +
" <desc>Today we have developed a nice alias tutorial.</desc>" +
" </wenzhang>" +
"</wenzhangList>" +
"</博客>";
XStream xstream = new XStream();
xstream.registerConverter(new MyConvertor());
xstream.processAnnotations(Blog.class);
Object o = xstream.fromXML(xmlContent);
System.out.println(o.getClass());
System.out.println(o);
}
输出:
Blog(writer=Author(name=张三), entries=[Entry(title=article1, description=My first blog entry.), Entry(title=article2, description=Today we have developed a nice alias tutorial.)])