1. 程式人生 > >Yaml介紹及JYaml將資料寫入yaml檔案

Yaml介紹及JYaml將資料寫入yaml檔案

一、簡介
        "YAML Ain't a Markup Language"(YAML不是一種置標語言)的遞迴縮寫,早先YAML的意思其實是:"Yet Another Markup Language"(另外一種置標語言),但為了強調這種語言以資料做為中心,而不是以置標語言為重點,而用返璞詞重新命名。
        YAML的官方定義很簡單,即“一種人性化的資料格式定義語言”,其主要功能用途類似於XML或JSON,YAML使用空白字元和分行來分隔資料,且巧妙避開各種封閉符號,如:引號、括號等,以避免這些符號在複雜層次結構中變得難以辨認。YAML的語法與高階語言類似,可以很簡單地表述*序列(java中的list)、雜湊表(java中的map)、標量(java中的基本型別等)資料結構,它重點強調可閱讀性。和GNU一樣,YAML是一個遞迴著說“不”的名字。不同的是,GNU對UNIX說不,YAML說不的物件是XML。

YAML vs XML:


YAML不是XML。為什麼不是XML呢?因為:
- YAML的可讀性好。
- YAML和指令碼語言的互動性好。
- YAML使用實現語言的資料型別。
- YAML有一個一致的資訊模型。
- YAML易於實現。

上面5條也就是XML不足的地方。同時,YAML也有XML的下列優點:
- YAML可以基於流來處理;
- YAML表達能力強,擴充套件性好。

YAML缺陷與不足:
YAML沒有自己的資料型別的定義,而是使用實現語言的資料型別。一個YAML檔案,在不同語言中解析後得到的資料型別可能會不同,由於其相容性問題,不同語言間的資料流轉不建議使用YAML。

總之,YAML試圖用一種比XML更敏捷的方式,來完成XML所完成的任務。
更多的內容及規範參見http://www.yaml.org。

YAML vs JSON:
JSON的語法其實是YAML的子集,大部分的JSON檔案都可以被YAML的剖析器剖析。雖然大部分的資料分層形式也可以使用類似JSON的格式,不過YAML並不建議這樣使用,除非這樣編寫能讓檔案可讀性增加,更重要的是,YAML的許多擴充套件在JSON是找不到的,如:進階資料形態、關係錨點、字串不需要引號、對映資料形態會儲存鍵值的順序等。

二、用途
指令碼語言
由於實現簡單,解析成本很低,YAML特別適合在指令碼語言中使用。列一下現有的語言實現:Ruby,Java,Perl,Python,PHP,OCaml,JavaScript,除了Java,其他都是指令碼語言。

序列化


YAML比較適合做序列化。因為它是宿主語言資料型別直轉的。

配置檔案
YAML做配置檔案也不錯。寫YAML要比寫XML快得多(**無需關注標籤或引號**),並且比ini文件功能更強。

除錯
由於其很強的閱讀性,用於除錯過程中dump出資訊供分析也是一種比較方便的做法。

二、下載
https://jaist.dl.sourceforge.net/project/jyaml/jyaml/jyaml-1.3/jyaml-1.3.jar

https://repo1.maven.org/maven2/org/jyaml/jyaml/1.3/jyaml-1.3.jar

http://www.java2s.com/Code/Jar/j/Downloadjyaml13dgujar.htm

三、YAML語法與範例
YAML包括Sequence和Map兩種基本資料格式。對於Sequence,其每一項會以“-”開頭。對於Map的每一項,key與value之間是用“:"來連線的。從屬關係則用空格來表示。這基本上就是YAML的全部語法了。

一般YAML副檔名為.yaml。比如:John.yaml

name: John Smith
age: 37
spouse:
    name: Jane Smith
    age: 25
children:
    -   name: Jimmy Smith
        age: 15
    -   name: Jenny Smith
        age 12
由於yaml的超強可讀性,我們瞭解到:John今年37歲,兩個孩子Jimmy 和Jenny活潑可愛,妻子Jane年輕美貌,而且年僅25歲,一個幸福的四口之家。

四、JYaml——YAML的Java實現
YAML已經有了不少實現,詳細的實現列表參見http://www.yaml.org/download.html。
其中JYaml(http://jyaml.sourceforge.net)是YAML的Java實現。

JYaml簡介
YAML的Java庫,現在可以用Java來讀取YAML。YAML是ruby常用的配置檔案,物件序列化格式,比XML輕便,易讀,比properties表達能力豐富。在ruby世界很受歡迎。現在有了Java的庫了。我覺得值得好好看一下,說不定以後可以用YAML取代Java世界的XML配置檔案了。
YAML使用實現語言的資料型別。我們看一下一些JYaml支援的Java資料型別:
- 原始資料和封裝類(比如int,java.lang.Integer)
- JavaBean相容物件(Structure支援)
- Collection(sequence支援)
# List
# Set
- Map(map支援)
- Arrays(sequence支援)
- BigInteger和BigDecimal
- Date

我們給出John.yaml的java描述:

package hehe;

public class Person {  
    private String name;  
    private int age;  
    private Person spouse;
    private Person[] children;  
    public Person(){
    }  
    public void setName(String name) {  
        this.name = name;  
    }  
    public void setAge(int age) {  
        this.age = age;  
    }  
    public void setSpouse(Person spouse) {  
        this.spouse = spouse;  
    }  
    public void setChildren(Person[] children) {  
        this.children = children;  
    }  
    public String getName() {  
        return this.name;  
    }  
    public int getAge() {  
        return this.age;  
    }  
    public Person getSpouse() {  
        return this.spouse;  
    }  
    public Person[] getChildren() {  
        return this.children;  
    }  
}
現在讓我們裝配一個Jone:
package hehe;

import java.io.File;
import java.io.FileNotFoundException;

import org.ho.yaml.Yaml;

public class TestYaml {
    public static void main(String[] args) throws Exception {
        /* Initialize data. */
        Person michael = new Person();
        Person floveria = new Person();
        Person[] children = new Person[2];
        children[0] = new Person();
        children[1] = new Person();
        
        michael.setName("Michael Corleone");
        michael.setAge(24);
        floveria.setName("Floveria Edie");
        floveria.setAge(24);  
        children[0].setName("boy");
        children[0].setAge(3);  
        children[1].setName("girl");  
        children[1].setAge(1);  
          
        michael.setSpouse(floveria);  
        floveria.setSpouse(michael);  
          
        michael.setChildren(children);  
        floveria.setChildren(children);  
          
        /* Export data to a YAML file. */  
        File dumpFile = new File("John.yaml");
        try {  
            Yaml.dump(michael, dumpFile);
        } catch (FileNotFoundException e) {  
            e.printStackTrace();  
        }
    }
}
下面我們看看John.yaml是什麼樣子:
--- &0 !hehe.Person
age: 24
children: &2 !hehe.Person[]
  - !hehe.Person
    age: 3
    name: boy
  - !hehe.Person
    age: 1
    name: girl
name: Michael Corleone
spouse: !hehe.Person
  age: 24
  children: *2
  name: Floveria Edie
  spouse: *0
其中!yaml.test.internal.Person是一些型別的資訊。load的時候需要用。
還可以用下面的程式碼dump出沒有型別資訊的John.yaml:
Yaml.dump(michael, dumpFile, true);

我們再來看看JYaml對流處理的支援。
為簡便起見,我們只是把同一個john寫2次:

import java.io.File;
import java.io.FileOutputStream;

import org.ho.yaml.Yaml;
import org.ho.yaml.YamlEncoder;

public class TestYaml {
	public static void main(String[] args) throws Exception {
		File dumpFile = new File("John.yaml");
		Person john2 = (Person) Yaml.loadType(dumpFile, Person.class);
		YamlEncoder enc = new YamlEncoder(new FileOutputStream(dumpFile));
		for(int i=0; i<2; i++){
			john2.setAge(37+i);
			enc.writeObject(john2);
			enc.flush();
		}
		enc.close();
	}
}
再看下John.yaml成這樣的了:
--- &0 !hehe.Person
age: 37
children: &2 !hehe.Person[]
  - !hehe.Person
    age: 3
    name: boy
  - !hehe.Person
    age: 1
    name: girl
name: Michael Corleone
spouse: !hehe.Person
  age: 24
  children: *2
  name: Floveria Edie
  spouse: *0
--- &9 !hehe.Person
age: 38
children: &11 !hehe.Person[]
  - !hehe.Person
    age: 3
    name: boy
  - !hehe.Person
    age: 1
    name: girl
name: Michael Corleone
spouse: !hehe.Person
  age: 24
  children: *11
  name: Floveria Edie
  spouse: *9
下面再把這兩個物件讀出來(注意while迴圈退出的方式):
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;

import org.ho.yaml.Yaml;
import org.ho.yaml.YamlDecoder;

public class TestYaml {
    public static void main(String[] args) throws Exception {
        File dumpFile = new File("John.yaml");
        Person john2 = (Person) Yaml.loadType(dumpFile, Person.class);
        YamlDecoder dec = new YamlDecoder(new FileInputStream(dumpFile));
        while(true){
        	try{
        	john2 = (Person) dec.readObject();
        	System.out.println(john2.getAge());
        	}catch(EOFException e){
                break;
            }
        }
    }
}
執行結果:
37
38

五、YAML和Ruby
        Ruby和YAML的聯絡,甚至比Java與XML的聯絡還要緊密。Ruby把YAML用到了和資料相關的方方面面。配置檔案的約定格式是YAML。同時YAML還是Ruby的文字序列化格式,就像XML是SDO的文字序列化格式一樣。
        不誇張的說,YAML是Ruby中流動的血液。
        那,Ruby為什麼不選XML呢?
        其一,XML對Ruby這樣的指令碼語言而言,解析起來比較困難。效率肯定是會有問題。Ruby的XML解析沒有完全實現,走的是實用主義的路線,可能也緣於此。而YAML要輕快很多。
        另外,XML使用自定義型別。就算解析出來,也不能直接用,還要再轉一次。而YAML不定義自己的型別,直接使用宿主語言本身型別,直截了當。
        YAML的Ruby實現出現的很早。但由於某些原因,YAML的Ruby實現是不嚴格的。隨著其他語言的YAML實現慢慢出現,這個問題日益明顯了。
        畢竟每個實現語言的型別不一樣的,不同語言文化也不同。因此不同實現的YAML的互動是個問題。相容性需要YAML規範來保證。而具體的實施還有很長的道路要走。
        但YAML在單一語言中,YAML的應用是沒有問題的。
        YAML在Ruby On Rails中經常用作配置檔案。比如資料庫資訊的配置:

test:
  adapter: mysql
  database: weblog_test
  username: root
  password:
  host: localhost
 
production:
  adapter: mysql
  database: weblog_production
  username: root
  password: 
  host: localhost
熟悉ROR的讀者應該能看出這是`config/database.yml'檔案。


六、YAML存在的意義

        無論多麼完美的事物,都需要有對立面,有說“NO”的聲音。XML也不例外。當然,站在主流的對立面,需要勇氣和智慧。
        YAML和XML不同,沒有自己的資料型別的定義,而是使用實現語言的資料型別。這一點,有可能是出奇制勝的地方,也可能是一個敗筆。如果相容性保證的不好的話,YAML資料在不同語言間流轉會有問題。如果相容性好的話,YAML就會成為不同語言間資料流通的橋樑。建議yaml.org設立相容認證機制,每個語言的實現必須通過認證。
        假如相容性沒問題的話,YAML就太完美了。輕巧,敏捷,高效,簡便,通用。這才是理想中的資料模型。當然就現在而言,這還只是個理想。

參考:(雖然我這裡標明的是轉載,但是我也把參考來的內容做了一些修改,費了我很大的工夫,尤其是一些程式碼按原文的方式來並不能成功執行,因為大部分內容是貼上過來的,雖然排版修改什麼的也花費了我大量的汗水但不想讓人質疑是偽原創就把我這篇文章型別定義為轉載,可是真是不甘心啊,因為我見網上標明轉載的文章那可真是一字不差的貼上過來啊。。。)
https://www.cnblogs.com/274914765qq/p/4483300.html
https://www.ibm.com/developerworks/cn/xml/x-cn-yamlintro/
http://blog.csdn.net/poechant/article/details/7017067

七、5分鐘認識YAML(一下內容來自http://blog.csdn.net/ruby_cn/article/details/177567)
第一分鐘:一個簡單的列表
你的朋友給你寄了一封如下的信件:
- Memoirs Found in a Bathtub
- Snow Crash
- Ghost World
同時,他也要求你按照上面的順序來閱讀這些書,他想和你探討關於這些書的內容。
在YAML中,這些有順序的列表(list)叫做序列(sequence)。序列包括了一組有順序的資料,當你在程式中載入這個序列,它們的順序保持不變。
在很多比較靈活的語言中,你可以用指定索引的方式訪問這個序列及其中的值。以0開頭的表示這個序列的第一項。
在PHP, Perl, 或 Ruby中,可以這樣:
print $book_list[0];
列印結果如下:
Memoirs Found in a Bathtub
這也是上面書籍列表的第一項。

其他列表
列表(Lists)可以包括各種資料:
---
- 1           # Numbers
- Snow Crash  # Strings
- 2003-07-24  # Dates
上面的列表以三個"-"開頭,這三個"-"是文件分割符(document separator),它表示了一段YAML文件的開始。當然,你也可以劃分多個文件,比如:

---
- Harry Potter and the Prisoner of Azkaban
- Harry Potter and the Goblet of Fire
- Harry Potter and the Order of the Phoenix
---
- Memoirs Found in a Bathtub
- Snow Crash
- Ghost World
第二分鐘:一個簡單的字典
當你沉浸在閱讀 Harry Potter 時,你的朋友又寄來了新的列表,這次列表包括了作者的名字:
Stanislaw Lem: Memoirs Found in a Bathtub
Neal Stephenson: Snowcrash
Daniel Clowes: Ghost World
上面的字典沒有順序,但是每個作者都對應一本書的名字。你的朋友希望你可以根據作者找到你想要的書。(注意前面沒有"-")
在YAML中,這樣的字典結構稱為 map,map通常用來存放成對的資料,冒號左面的稱為key,右面的叫做value。 
key 用作從map中取資料的索引。
在PHP 或 Ruby中,可以這樣寫:
print $books_by_author['Daniel Clowes'];
將列印如下內容:
Ghost World

Sequence中的Map
你的朋友寄給你了一份更新的列表,包括了作者,書名,並且有一定的順序,希望你能按照這個順序來閱讀。
---
- Stanislaw Lem: Memoirs Found in a Bathtub
- Neal Stephenson: Snowcrash
- Daniel Clowes: Ghost World
上面的序列中,每一項都是一個map,當一種集合(collection )包括另一種集合時,稱作巢狀(nesting)。

第三分鐘:郵箱裡的東西
第三分鐘我們先放慢一下速度,老師讓你朋友這麼打擾你我想過不多久你肯定會被氣瘋的。
冉我們來看看你的信箱。你住在一個公寓裡,你的信箱只有一個小口可以投信,旁邊是其他的幾百個信箱。
你檢查一下自己的信箱發現了五封信。兩封是給你的鄰居Jim O'Connor的,一封是給一個叫做"Hank Bros.: the Car Wash!"的公司的,其他的兩封信是你自己的。
你打算對自己郵箱裡多出來的信進行投訴,你打算給郵局傳送一封信,讓他們看看這給你帶來了多大的麻煩。
你開始了一個新的YAML文件,裡面包括今天的日期,和你的信箱裡的信件:
---
date: 2003-07-25
letters to:
  "Hank Bros.: the Car Wash!": 1
  Jim O'Connor: 2
  Myself: 2
這個例子裡,一個map嵌套了另一個map,第一個MAP的key是,它的值是letters to  一個被巢狀的MAP,第二個MAP是縮排排列的。
在PHP或者RUBY中,可以這樣訪問:
print $mailbox['letters to']['Myself'];
將會列印:

你也發現了,"Hank Bros.: the Car Wash!"用雙銀號擴起來了,這是因為這裡麵包括了冒好。否則,會引起麻煩的,你將可能弄不清哪個是key 哪個是value。

第四分鐘:更多訊息
你從郵局得到了一些解釋:

---
Concerning Car Washes: >

  We are sorry to have misplaced this letter.
  We were told by a reliable source that you
  were the owner of "Hank Bros.: the Car Wash!".

  Sorry.
郵局工作有誤,但這也向我們展示了YAML的另一個特點:塊(block )。在他們的資訊裡,用到了block。
在YAML中,一個block是一段文字,它們縮排一個或多個空格。
注意這個大於號: >。它表示一個“folded block”的開始。所謂folded block,就是一行一行緊挨著句子將組成一個段落,這個段落就像我們讀課文那樣可以閱讀的。
上面的訊息在YAML 閱讀器看來將分為兩行,由一個回車換行分割。

Controlling Words
上面的訊息還沒完,這是後面的:

Concerning "Jim O'Connor": |

  You are receiving Jim O'Connor's mail for several reasons:
  - The nameplate on your mailbox still says his name.
  - He has told our postman that you screen his mail.
  - He is living in your ceiling.
  - He held a raygun to the postmans head.
這看起來有點古怪,郵局的人用一個豎線,取代了原來的大於號,這有什麼不同嗎?
一個豎線(也稱管道pipe),表示了一個literal block的開始。 literal block 表示每個句子自成一節,這個塊將會像計算機讀取那樣,都是字面值。
YAML 閱讀器會把上面的塊當作5行(第一行是空行)。

第五分鐘:這就是全部嗎?
到現在,看起來YAML也就這麼多東西而已,它只是一些列表和字典組成的集合而已?
這只是我們五分鐘能談論到的最基本的東西,在繼續之前,來看最後一個概念。
這裡是一個人讀這個五分鐘教程所用時間的紀錄:

---
name: Dirk Hockeybranch
minutes spent: 
 - 1.02
 - 1.34
 - 0.7
 - 0.89
 - 0.94
上面我們用一個序列 (sequence)巢狀在了一個map中,序列中的每項代表了在第幾步所用的時間。如果每個人都把自己閱讀的時間像這樣寄給我們,那我們需要給本教程準備一個更準確的名字,比如YAML in Five-Point-One-Two Minutes。這將有點華而不實。(我們可能需要很多的篇幅來寫這樣的文件。)
一個機靈的YAML使用者可能會用 inline sequence來記錄它們所用的時間:
---
name: Dirk Hockeybranch
minutes spent: [1.02, 1.34, 0.7, 0.89, 0.94]
對於短的列表,你可以把它們都房在一行裡,各項之間用逗號分割,最後用中括號括起來。
Inline maps 也類似:
---
minutes spent: {one: 1.02, two: 1.34, three: 0.7,
                four: 0.89, five: 0.94}
以上也要謹慎使用,BLOCK在inline sequences 或 Inline maps 中不能使用。