XML文件解析之XStream的初步使用

Xstream個人覺得是一個挺簡單的XML解析工具,使用一些註解就可以簡單完成xml數據和java實體之間的轉換。sxtream還支持對流的操作。這裏只講簡單的字符串數據和實體之間的轉換。

一、依賴

        <dependency>
            <groupId>com.thoughtworks.xstream</groupId>
            <artifactId>xstream</artifactId>
            <version>1.4.11.1</version>
        </dependency>

二、一些常用的註解

這邊只講自己用到,比較熟悉的3個註解,後面如果有使用到其餘的註解在補充

1、@XStreamAlias 用於實體類或者字段上,可以設置別名,相當於alias方法。

/**
 * Annotation used to define an XStream class or field alias.
 * @see com.thoughtworks.xstream.XStream#alias(String, Class)
 * @see com.thoughtworks.xstream.XStream#alias(String, Class, Class)
 * @see com.thoughtworks.xstream.XStream#addDefaultImplementation(Class, Class)
 */

2、@XStreamAsAttribute

把字段作爲xml文件節點中的屬性

3、@XStreamImplicit

可以去除節點中的集合或數組元素

4、@XStreamConverter

可以使用我們自定義的格式來轉換,如date轉爲各種樣式的string

三、使用

先簡單建立一個班級-學生關係的類。

SchoolClass.java
@Data
public class SchoolClass {

    private String className;

    private String classMaster;

    private Song song;

    private List<Student> studentList;
}


Song.java
@Data
public class Song {

    private String name;

    private String author;
}

Student.java
@Data
public class Student {

    private int id;

    private String name;

    private int age;

    private Date birthDay;
}

1、類轉字符串

這邊賦值後轉成xml

public class SchoolXmlTest {

    public static void main(String[] args) {
        beanToString();
    }

    public static void beanToString(){

        SchoolClass class1 = new SchoolClass();
        class1.setClassName("班級1");
        class1.setClassMaster("班主任1");
        Song classSong = new Song();
        classSong.setName("班歌");
        class1.setSong(classSong);
        List<Student> students = new ArrayList<>();
        class1.setStudentList(students);
        Student student1 = new Student();
        student1.setId(1);
        student1.setName("學生1");
        student1.setAge(19);
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.YEAR, 2000);
        calendar.set(Calendar.MONTH, 0);
        calendar.set(Calendar.DATE, 1);
        student1.setBirthDay(calendar.getTime());
        students.add(student1);
        Student student2 = new Student();
        student2.setId(2);
        student2.setName("學生2");
        student2.setAge(16);
        students.add(student2);

        XStream stream = new XStream();
        String result = stream.toXML(class1);
        System.out.println(result);
    }
}

打印的結果如下,肯定不是我們想要的數據

<xml.SchoolClass>
  <className>班級1</className>
  <classMaster>班主任1</classMaster>
  <song>
    <name>班歌</name>
  </song>
  <studentList>
    <xml.Student>
      <id>1</id>
      <name>學生1</name>
      <age>17</age>
      <birthDay>2000-01-01 03:07:42.300 UTC</birthDay>
    </xml.Student>
    <xml.Student>
      <id>2</id>
      <name>學生2</name>
      <age>16</age>
    </xml.Student>
  </studentList>
</xml.SchoolClass>

改一下SchoolClass和Student的類

@Data
@XStreamAlias("class")
public class SchoolClass {

    @XStreamAlias("class_name")
    private String className;

    private String classMaster;

    private Song song;

    @XStreamAlias("STUDENT")
//    @XStreamImplicit
    private List<Student> studentList;
}

@Data
@XStreamAlias("student")
public class Student {

    @XStreamAsAttribute
    private int id;

    private String name;

    private int age;

    @XStreamConverter(DateConverter.class)
    private Date birthDay;
}

我們想把Date按照我們想要的格式轉爲字符串,那就需要自定義一個轉換的規則


public class DateConverter implements Converter {

    /**
     * bean轉爲xml
     * @param source
     * @param writer
     * @param context
     */
    @Override
    public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
        Date date = (Date)source;
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        String time = format.format(date);
        writer.setValue(time);
    }

    /**
     * xml轉成bean
     * @param reader
     * @param context
     * @return
     */
    @Override
    public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
        if (StringUtils.isEmpty(reader.getValue())){
            return null;
        }
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        try {
            return format.parse(reader.getValue());
        } catch (ParseException e) {
            return null;
        }
    }

    @Override
    public boolean canConvert(Class type) {
        return Date.class == type;
    }
}

2、如果date字段作爲屬性,那麼就要使用另外一種轉換,並且要加上@XStreamAsAttribute註解

public class DateValueConverter implements SingleValueConverter {

    @Override
    public String toString(Object obj) {
        Date date = (Date)obj;
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        String time = format.format(date);
        return time;
    }

    @Override
    public Object fromString(String str) {
        if (StringUtils.isEmpty(str)){
            return null;
        }
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        try {
            return format.parse(str);
        } catch (ParseException e) {
            return null;
        }
    }

    @Override
    public boolean canConvert(Class type) {
        return Date.class == type;
    }
}

轉換方法處在改動一下

        //new Xpp3Driver(new NoNameCoder())如果使用new XStream()構造,下劃線_會變成__
        XStream stream = new XStream(new Xpp3Driver(new NoNameCoder()));
        //使用註解解析要設置爲true
        stream.autodetectAnnotations(true);
        //@XStreamAlias相當於alias方法
//        stream.alias("class", SchoolClass.class);
        //有時候解析會在屬性裏出現class,<STUDENT class="singleton-list">,去除class
        stream.aliasSystemAttribute(null,"class");
        String result = stream.toXML(class1);
        System.out.println(result);

結果如下

<class>
  <class_name>班級1</class_name>
  <classMaster>班主任1</classMaster>
  <song>
    <name>班歌</name>
  </song>
  <STUDENT>
    <student id="1">
      <name>學生1</name>
      <age>17</age>
      <birthDay>2000-01-01</birthDay>
    </student>
  </STUDENT>
</class>

@XStreamImplicit註解可以去除上面的STUDENT節點,可以根據實際情況使用。

2、字符串轉實體類

    public static void xmlToBean(){
        String xml = "<class>\n" +
                "  <class_name>班級1</class_name>\n" +
                "  <classMaster>班主任1</classMaster>\n" +
                "  <song>\n" +
                "    <name>班歌</name>\n" +
                "  </song>\n" +
                "  <STUDENT>\n" +
                "    <student id=\"1\">\n" +
                "      <name>學生1</name>\n" +
                "      <age>17</age>\n" +
                "    </student>\n" +
                "  </STUDENT>\n" +
                "</class>";
        XStream xStream = new XStream();
        xStream.autodetectAnnotations(true);
        //設置要轉換的類
        xStream.processAnnotations(SchoolClass.class);
        SchoolClass schoolClass = (SchoolClass) xStream.fromXML(xml);
        System.out.println(schoolClass);
    }

打印結果

Security framework of XStream not initialized, XStream is probably vulnerable.
SchoolClass(className=班級1, classMaster=班主任1, song=Song(name=班歌, author=null), studentList=[Student(id=1, name=學生1, age=17)])

控制檯打印結果有個安全警告,我們可以通過下面的方式處理

        XStream xStream = new XStream();
        XStream.setupDefaultSecurity(xStream);
        //自己實體類的路徑
        xStream.allowTypesByWildcard(new String[]{"xml.*"});
        xStream.autodetectAnnotations(true);
        xStream.processAnnotations(SchoolClass.class);
        SchoolClass schoolClass = (SchoolClass) xStream.fromXML(xml);
        System.out.println(schoolClass);

三、整理成工具類

把xml和實體之間的轉換弄成一個工具類。

public class XmlUtils {

    public static String beanToString(Object o){
        XStream xStream = new XStream(new Xpp3Driver(new NoNameCoder()));
        xStream.aliasSystemAttribute(null,"class");
        xStream.autodetectAnnotations(true);
        String result = xStream.toXML(o);
        return result;
    }


    public static <T>T stringToClass(String xml, Class<T> clazz){
        XStream xStream = new XStream();
        XStream.setupDefaultSecurity(xStream);
        //自己實體類的路徑
        xStream.allowTypesByWildcard(new String[]{"xml.*"});
        xStream.autodetectAnnotations(true);
        xStream.processAnnotations(clazz);
        return clazz.cast(xStream.fromXML(xml));
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章