xstream 解析xml工具

官网:https://x-stream.github.io/

<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.)])

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值