天天看點

總結使用SnakeYAML解析與序列化YAML相關

SnakeYAML官網

https://bitbucket.org/asomov/snakeyaml/wiki/Documentation#markdown-header-installation

一、pom.xml中添加依賴

<dependency>
    <groupId>org.yaml</groupId>
    <artifactId>snakeyaml</artifactId>
    <version>1.25</version>
</dependency>
           

二、Yaml基礎知識

1 基本文法規則如下

大小寫敏感;

使用縮進表示層級關系;

縮進時不允許使用Tab鍵,隻允許使用空格;

縮進的空格數目不重要,隻要相同層級的元素左側對齊即可;

 2 基本資料結構有三種

對象:鍵值對的集合,又稱為映射(mapping)/ 哈希(hashes) / 字典(dictionary)

數組:一組按次序排列的值,又稱為序列(sequence) / 清單(list)

一組連詞線開頭的行,構成一個數組;

純量(scalars):單個的、不可再分的值

 3 複合結構

languages:
 - Ruby
 - Perl
 - Python 
websites:
 YAML: yaml.org 
 Ruby: ruby-lang.org 
 Python: python.org 
 Perl: use.perl.org 
           

4 錨點和引用

錨點&和别名*,可以用來引用;

&用來建立錨點(defaults),<<表示合并到目前資料,*用來引用錨點;

defaults: &defaults
  adapter:  postgres
  host:     localhost

development:
  database: myapp_development
  <<: *defaults

test:
  database: myapp_test
  <<: *defaults
           

就等價于:

defaults:
  adapter:  postgres
  host:     localhost

development:
  database: myapp_development
  adapter:  postgres
  host:     localhost

test:
  database: myapp_test
  adapter:  postgres
  host:     localhost
           

三、自定義類型解析

public class CarWithWheel {
    private String plate;
    private String year;
    private Wheel wheel;
    private List<Wheel> listWheels;
    private Map<Wheel, Date> mapWheels;
    private Object part;
    public String getPlate() {
        return plate;
    }
    public void setPlate(String plate) {
        this.plate = plate;
    }
    public String getYear() {
        return year;
    }
    public void setYear(String year) {
        this.year = year;
    }
    public Wheel getWheel() {
        return wheel;
    }
    public void setWheel(Wheel wheel) {
        this.wheel = wheel;
    }
    public List<Wheel> getListWheels() {
        return listWheels;
    }
    public void setListWheels(List<Wheel> listWheels) {
        this.listWheels = listWheels;
    }
    public Map<Wheel, Date> getMapWheels() {
        return mapWheels;
    }
    public void setMapWheels(Map<Wheel, Date> mapWheels) {
        this.mapWheels = mapWheels;
    }
    public Object getPart() {
        return part;
    }
    public void setPart(Object part) {
        this.part = part;
    }
}
           
public class Wheel {
    private int id;
    private String name;
    public Wheel() {
    }
    public Wheel(int id, String name) {
        this.id = id;
        this.name = name;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}
           

 //Customer這個對象 載入多個檔案的時候會用到

public class Customer {
    private String firstName;
    private String lastName;
    private int age;
    // getters and setters
}
           

四、生成YAML檔案

1 準備pojo資料階段:

CarWithWheel car1 = new CarWithWheel();
//plate
car1.setPlate("plate test");
//year
car1.setYear("2020");
//wheel
Wheel wheel = new Wheel();
wheel.setId(1);
wheel.setName("baoma");
car1.setWheel(wheel);
//listWheel
ArrayList<Wheel> listWheels = new ArrayList<>();
Wheel wheel2 = new Wheel(2,"benchi");
Wheel wheel3 = new Wheel(3,"dazhong");
Wheel wheel4 = new Wheel(4,"fengtian");
listWheels.add(wheel2);
listWheels.add(wheel3);
listWheels.add(wheel4);
car1.setListWheels(listWheels);
//mapWheels
Map<Wheel, Date> mapWheels = new HashMap<>();
Wheel wheel5 = new Wheel(5,"changcheng");
Wheel wheel6 = new Wheel(6,"jili");
Wheel wheel7 = new Wheel(7,"byd");
mapWheels.put(wheel5,new Date());
mapWheels.put(wheel6,new Date());
mapWheels.put(wheel7,new Date());
car1.setMapWheels(mapWheels);
//part
Wheel wheel8 = new Wheel(8,"part test");
car1.setPart(wheel8);
           

2 根據pojo生成yaml檔案:

//real logic
 Constructor constructor = new Constructor(CarWithWheel.class);//root
TypeDescription carDescription = new TypeDescription(CarWithWheel.class);
carDescription.addPropertyParameters("listWheels", Wheel.class);
carDescription.addPropertyParameters("mapWheels", Wheel.class, Object.class);
constructor.addTypeDescription(carDescription);
//Representer
Representer representer = new Representer();
representer.addClassTag(CarWithWheel.class, Tag.MAP);
representer.addClassTag(Wheel.class, Tag.MAP);
//DumperOptions
// DumperOptions options = new DumperOptions();
// options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
// options.setCanonical(false); // display bean member attribute
// options.setExplicitStart(true); // display --- start
//入口
Yaml yaml = new Yaml(constructor,representer);
// Yaml yaml = new Yaml(constructor);
// Yaml yaml = new Yaml(constructor,representer);
yaml.dump(car1,new FileWriter(new File(car_with_wheel)));
           

3 最終生成的yaml檔案如下圖所示:

listWheels:
- {id: 2, name: benchi}
- {id: 3, name: dazhong}
- {id: 4, name: fengtian}
mapWheels:
  ? {id: 5, name: changcheng}
  : 2021-04-26T07:21:09.611Z
  ? {id: 6, name: jili}
  : 2021-04-26T07:21:09.611Z
  ? {id: 7, name: byd}
  : 2021-04-26T07:21:09.611Z
part: {id: 8, name: part test}
plate: plate test
wheel: {id: 1, name: baoma}
year: '2020'
           

五、文檔解析

SnakeYAML

支援從

String

InputStream

加載文檔

1 加載單一的檔案

情形一:Yaml.load() accepts a String

public void loadString() {
    Yaml yaml = new Yaml();
    String document = "hello: 25";
    Object load = yaml.load(document);
    System.out.println(load);
}
           

情形二:Yaml.load() accepts a InputStream

public void loadStream() {
     Yaml yaml = new Yaml();
     Object load = yaml.load(new FileInputStream(new File(yaml_path)));
     System.out.println(load);
}
           

2 載入多個檔案

假設下面的内容在一個檔案中:

---
firstName: "John"
lastName: "Doe"
age: 20
---
firstName: "Jack"
lastName: "Jones"
age: 25
           
//Yaml.loadAll()
//If a String or a stream contains several documents, you may load them all with the Yaml.loadAll() method.
public void loadAll()  {
        Yaml yaml = new Yaml();
        Iterable<Object> objects = yaml.loadAll(new FileInputStream(new File(yaml_path_two)));
        Object load = objects;
        int counter = 0;
        for (Object data : objects) {
            System.out.println(data);
            counter++;
        }
        System.out.println(counter);
    }
           

3 簡單的自定義類型解析

 自定義類型解析:自定義的簡單的pojo --Customer 

// Yaml yaml = new Yaml(new Constructor(Customer.class));
   Yaml yaml = new Yaml(new Constructor(Customer.class));
   Customer customer = yaml.load(new FileInputStream(new File(yaml_path)));
   System.out.println(customer);
           

4 嵌套對象

以剛才生成的car_with_wheel.yaml為例進行解析

listWheels:
- {id: 2, name: benchi}
- {id: 3, name: dazhong}
- {id: 4, name: fengtian}
mapWheels:
  ? {id: 5, name: changcheng}
  : 2021-04-26T07:21:09.611Z
  ? {id: 6, name: jili}
  : 2021-04-26T07:21:09.611Z
  ? {id: 7, name: byd}
  : 2021-04-26T07:21:09.611Z
part: {id: 8, name: part test}
plate: plate test
wheel: {id: 1, name: baoma}
year: '2020'
           

核心代碼:

Constructor constructor = new Constructor(CarWithWheel.class);//root
Yaml yaml = new Yaml(constructor);
CarWithWheel carWithWheel =(CarWithWheel)yaml.load(new FileInputStream(new File(car_with_wheel)));
System.out.println(carWithWheel);
           

整個代碼:

public void complexYaml(){
        //為了能正确解析,我們可以在頂級類上為給定屬性指定TypeDescription
        Constructor constructor = new Constructor(CarWithWheel.class);//root
//        TypeDescription carDescription = new TypeDescription(CarWithWheel.class);
//        carDescription.addPropertyParameters("listWheels", Wheel.class);
//        carDescription.addPropertyParameters("mapWheels", Wheel.class, Object.class);
//        constructor.addTypeDescription(carDescription);
        //
        Yaml yaml = new Yaml(constructor);
        CarWithWheel carWithWheel =(CarWithWheel)yaml.load(new FileInputStream(new File(car_with_wheel)));
        System.out.println(carWithWheel);
//        carWithWheel.getListWheels().forEach(System.out::println);
//        for(Map.Entry entry:carWithWheel.getMapWheels().entrySet()){
//            System.out.println("key: " + entry.getKey()+","+"value: "+ entry.getValue());
//        }
//        System.out.println(carWithWheel.getPart());
//        System.out.println(carWithWheel.getPlate());
//        System.out.println(carWithWheel.getWheel());
//        System.out.println(carWithWheel.getYear());
    }
           

5 特殊的錨點處理

錨點&和别名*,可以用來引用;

&用來建立錨點(defaults),<<表示合并到目前資料,*用來引用錨點;

anchor.yaml内容如下圖所示:

invoice: 34843
date   : 2001-01-23
billTo: &id001
    given  : Chris
    family : Dumars
    address:
        lines: |
            458 Walkman Dr.
            Suite #292
        city    : Royal Oak
        state   : MI
        postal  : 48046
shipTo: *id001
product:
    - sku         : BL394D
      quantity    : 4
      description : Basketball
      price       : 450.00
    - sku         : BL4438H
      quantity    : 1
      description : Super Hoop
      price       : 2392.00
tax  : 251.42
total: 4443.52
comments:
    Late afternoon is best.
    Backup contact is Nancy
    Billsmer @ 338-4338.
           

加載yaml代碼:

public void anchorYaml() throws FileNotFoundException {
   Yaml yaml = new Yaml();
   Object load = yaml.load(new FileInputStream(new File(anchor)));
   System.out.println(load);
}
           

最終的load的字元串:

{
    invoice = 34843,
    date = Tue Jan 23 08: 00: 00 CST 2001,
    billTo = {
        given = Chris,
        family = Dumars,
        address = {
            lines = 458 Walkman Dr.
                Suite # 292,
            city = Royal Oak,
            state = MI,
            postal = 48046
        }
    },
    shipTo = {
        given = Chris,
        family = Dumars,
        address = {
            lines = 458 Walkman Dr.
                Suite # 292,
            city = Royal Oak,
            state = MI,
            postal = 48046
        }
    },
    product = [{
            sku = BL394D,
            quantity = 4,
            description = Basketball,
            price = 450.0
        }, {
            sku = BL4438H,
            quantity = 1,
            description = Super Hoop,
            price = 2392.0
        }
    ],
    tax = 251.42,
    total = 4443.52,
    comments = Late afternoon is best.Backup contact is Nancy Billsmer @ 338 - 4338.
}
           

六、踩過的幾個坑

1  生成的yaml檔案中包含packName.className

!!com.example.rts_test.entity.CarWithWheel
listWheels:
- {id: 2, name: benchi}
- {id: 3, name: dazhong}
- {id: 4, name: fengtian}
mapWheels:
  ? {id: 7, name: byd}
  : 2021-04-26T09:00:19.727Z
  ? {id: 5, name: changcheng}
  : 2021-04-26T09:00:19.727Z
  ? {id: 6, name: jili}
  : 2021-04-26T09:00:19.727Z
part: !!com.example.rts_test.entity.Wheel {id: 8, name: part test}
plate: plate test
wheel: {id: 1, name: baoma}
year: '2020'
           

解決方法:增加Representer

before:

//real logic
Constructor constructor = new Constructor(CarWithWheel.class);//root
TypeDescription carDescription = new TypeDescription(CarWithWheel.class);
carDescription.addPropertyParameters("listWheels", Wheel.class);
carDescription.addPropertyParameters("mapWheels", Wheel.class, Object.class);
constructor.addTypeDescription(carDescription);
//入口
Yaml yaml = new Yaml(constructor);
yaml.dump(car1,new FileWriter(new File(car_with_wheel)));
           

after:

//real logic
Constructor constructor = new Constructor(CarWithWheel.class);//root
TypeDescription carDescription = new TypeDescription(CarWithWheel.class);
carDescription.addPropertyParameters("listWheels", Wheel.class);
carDescription.addPropertyParameters("mapWheels", Wheel.class, Object.class);
constructor.addTypeDescription(carDescription);
//Representer
Representer representer = new Representer();
representer.addClassTag(CarWithWheel.class, Tag.MAP);
representer.addClassTag(Wheel.class, Tag.MAP);
Yaml yaml = new Yaml(constructor,representer);
yaml.dump(car1,new FileWriter(new File(car_with_wheel)));
           

最終結果:

listWheels:
- {id: 2, name: benchi}
- {id: 3, name: dazhong}
- {id: 4, name: fengtian}
mapWheels:
  ? {id: 5, name: changcheng}
  : 2021-04-26T09:03:11.229Z
  ? {id: 7, name: byd}
  : 2021-04-26T09:03:11.229Z
  ? {id: 6, name: jili}
  : 2021-04-26T09:03:11.229Z
part: {id: 8, name: part test}
plate: plate test
wheel: {id: 1, name: baoma}
year: '2020'
           

2 項目中有需要,load之後需要保留yaml中的錨點,這個還在編碼測試中