YAML 試圖用一種比 XML 更敏捷的方式,來(lái)完成 XML 所完成的任務(wù)。
發(fā)布日期: 2007 年 2 月 02 日
級別: 中級
訪(fǎng)問(wèn)情況 2629 次瀏覽
建議: 0 (添加評論)
YAML Ain't Markup Language
和GNU一樣,YAML是一個(gè)遞歸著(zhù)說(shuō)“不”的名字。不同的是,GNU對UNIX說(shuō)不,YAML說(shuō)不的對象是XML。
YAML不是XML。

為什么不是XML呢?因為:
上面5條也就是XML不足的地方。同時(shí),YAML也有XML的下列優(yōu)點(diǎn):
總之,YAML試圖用一種比XML更敏捷的方式,來(lái)完成XML所完成的任務(wù)。
更多的內容及規范參見(jiàn)http://www.yaml.org。
Structure通過(guò)空格來(lái)展示。Sequence里的項用"-"來(lái)代表,Map里的鍵值對用":"分隔.
這幾乎就是所有的語(yǔ)法了.
一般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 |
John今年37歲,有一個(gè)幸福的四口之家。兩個(gè)孩子Jimmy 和Jenny活潑可愛(ài)。妻子Jane年輕美貌。
如果深入研究的話(huà)還可能發(fā)現一些社會(huì )問(wèn)題。
可見(jiàn)YAML的可讀性是不錯。
YAML已經(jīng)有了不少實(shí)現,詳細的實(shí)現列表參見(jiàn)http://www.yaml.org/download.html。
其中JYaml(http://jyaml.sourceforge.net)是YAML的Java實(shí)現。
YAML使用實(shí)現語(yǔ)言的數據類(lèi)型。我們看一下一些JYaml支持的Java數據類(lèi)型:
我們給出John.yaml的java描述:
public class Person { private String name; private int age; private Person sponse; private Person[] children; // setXXX, getXXX方法略. } |
現在讓我們裝配一個(gè)Jone:
Person john = new Person(); john.setAge(37); john.setName("John Smith"); Person sponse = new Person(); sponse.setName("Jane Smith"); sponse.setAge(25); john.setSponse(sponse); Person[] children = {new Person(), new Person()}; children[0].setName("Jimmy Smith"); children[0].setAge(15); children[1].setName("Jenny Smith"); children[1].setAge(12); john.setChildren(children); |
使用JYaml把Jone“Dump”出來(lái):
File dumpfile = new File("John_dump.yaml"); Yaml.dump(john, dumpfile); |
下面我們看看John_dump.yaml是什么樣子:
--- !yaml.test.internal.Person age: 37 children: !yaml.test.internal.Person[] - !yaml.test.internal.Person age: 15 name: Jimmy Smith - !yaml.test.internal.Person age: 12 name: Jenny Smith name: John Smith sponse: !yaml.test.internal.Person age: 25 name: Jane Smith |
其中!yaml.test.internal.Person是一些類(lèi)型的信息。load的時(shí)候需要用。
現在用JYaml把Jone_dump.yaml load進(jìn)來(lái):
Person john2 = (Person) Yaml.loadType(dumpfile, Person.class); |
還可以用下面的代碼dump出沒(méi)有類(lèi)型信息的John.yaml:
Yaml.dump(john,dumpfile, true); |
我們再來(lái)看看JYaml對流處理的支持。
為簡(jiǎn)便起見(jiàn),我們只是把同一個(gè)john寫(xiě)10次:
YamlEncoder enc = new YamlEncoder(new FileOutputStream(dumpfile)); for(int i=0; i<10; i++){ john.setAge(37+i); enc.writeObject(john); enc.flush(); } enc.close(); |
下面再把這十個(gè)對象一個(gè)一個(gè)讀出來(lái)(注意while循環(huán)退出的方式):
YamlDecoder dec = new YamlDecoder(new FileInputStream(dumpfile)); int age = 37; while(true){ try{ john = (Person) dec.readObject(); assertEquals(age, john.getAge()); age++; }catch(EOFException eofe){ break; } } |
JYaml的源碼是基于JVM5的。也提供對JVM5功能的一些支持,比如對枚舉類(lèi)型的支持。
JYaml的JVM14的支持是通過(guò)retrotranslator(http://retrotranslator.sourceforge.net). retrotranslator使用ASM(http://asm.objectweb.org )動(dòng)態(tài)修改JVM5字節碼成JVM14,使JYaml在JVM14下可用。會(huì )犧牲一些效率。Debug時(shí)也會(huì )失真。
我修改了JYaml作者Toby Ho的一些源碼(基于Beta2.2),使之與JVM14兼容。當然,為此也去掉了對JVM5才有的類(lèi)型的支持(比如枚舉類(lèi)型). 這個(gè)犧牲應該是值得的。需要的人可以來(lái)信索取。
如果讀者想了解更多SDO的概念,請參見(jiàn)http://www.ibm.com/developerworks/library/specification/j-commonj-sdowmt/index.html 。這里我們只討論SDO的java實(shí)現。
SDO里有一個(gè)很重要的概念,DataObject。從DataObject的接口里,可以看出有兩類(lèi)的數據類(lèi)型,一類(lèi)是JYaml支持的,諸如String, List之類(lèi),一類(lèi)是DataObject。因此我們只需要增加一種支持DataObject的類(lèi)型,就可以完成對SDO DataObject的支持。
不妨叫YamlDataObject。下面是它的數據結構。
public class YamlDataObject { private String uRI; private String name; private Map attributes; } |
YamlDataObject里用到的類(lèi)型,都是JYaml能夠支持的。其中,uRI和name用來(lái)描述DataObject自身。attributes來(lái)描述DataObject里的值。這些值可能為DataObject,也可能是JYaml所支持的其他類(lèi)型。我們希望用YamlDataObject來(lái)?yè)擠ataObject的角色,因此YamlDataObject和DataObject要能互相轉化。增加兩個(gè)API:
private static YamlDataObject newInstance(DataObject dataobject){... private DataObject toDataObject() {... |
上面兩個(gè)之所以是private的,是因為我們打算遵照JYaml的習慣,增加兩個(gè)接口,dump和load,這樣newInstance和toDataObject只在內部使用了。下面是dump和load的定義:
public static void dump(DataObject dataobject, File dumpfile) throws FileNotFoundException{... public static Object load(File file) throws FileNotFoundException{... |
下面是一小段測試代碼:
// ... create dataobject ... File bodump=new File("bodump.yaml"); YamlDataObject.dump(dataobject,bodump); assertTrue(bodump.exists()); DataObject dataobject2 = (DataObject) YamlDataObject.load(bodump); // check result assertNotNull(dataobject2); for(int i=0; i<5; i++){ assertEquals(dataobject.get(i),dataobject2.get(i)); } |
具體的實(shí)現細節就不談了。讀者可以作為練習題,熟悉一下這兩個(gè)數據整合方案。
Ruby和YAML的聯(lián)系,甚至比Java與XML的聯(lián)系還要緊密。Ruby把YAML用到了和數據相關(guān)的方方面面。配置文件的約定格式是YAML。同時(shí)YAML還是Ruby的文本序列化格式,就像XML是SDO的文本序列化格式一樣。
不夸張的說(shuō),YAML是Ruby中流動(dòng)的血液。
那,Ruby為什么不選XML呢?
其一,XML對Ruby這樣的腳本語(yǔ)言而言,解析起來(lái)比較困難。效率肯定是會(huì )有問(wèn)題。Ruby的XML解析沒(méi)有完全實(shí)現,走的是實(shí)用主義的路線(xiàn),可能也緣于此。而YAML要輕快很多。
另外,XML使用自定義類(lèi)型。就算解析出來(lái),也不能直接用,還要再轉一次。而YAML不定義自己的類(lèi)型,直接使用宿主語(yǔ)言本身類(lèi)型,直截了當。
下面給一個(gè)Ruby YAML的簡(jiǎn)介。
我們使用Ruby寫(xiě)一個(gè)Person類(lèi):
class Person attr_accessor :name, :age, :sponse, :children def initialize(name, age, sponse=nil, children=nil) @name = name @age = age @sponse = sponse @children = children end end |
把John裝配起來(lái):
jane = Person.new("Jane Smith", 25) children = [Person.new("Jimmy Smith", 15), Person.new("Jenny Smith", 12)] john = Person.new("John Smith", 37, jane, children) |
Dump出John到John.yaml:
File.open('John_ruby.yaml', 'w') do |os| YAML::dump(john, os) end |
我們看看Dump的結果:
--- !ruby/object:Person age: 37 children: - !ruby/object:Person age: 15 children: name: Jimmy Smith sponse: - !ruby/object:Person age: 12 children: name: Jenny Smith sponse: name: John Smith sponse: !ruby/object:Person age: 25 children: name: Jane Smith sponse: |
仔細觀(guān)察會(huì )發(fā)現和JYaml Dump出來(lái)的還是有些區別的。
首先類(lèi)型信息不一樣。這還好說(shuō),雖然Ruby load Java的不太可能,但Java加載Ruby的應該沒(méi)問(wèn)題。
還有就是一些實(shí)現上細微的區別。比如Ruby::Yaml的"spone:",JYaml認為是不合法的。還有children的不縮行。盡管Ruby::Yaml很流行,但JYaml認為這是Ruby的Bug。網(wǎng)上有相應的爭論。
不過(guò)在一個(gè)語(yǔ)言里使用還是沒(méi)問(wèn)題的。下面是load John的代碼:
john2 = File.open('John.yaml') { |is| YAML::load(is) } |
YAML的Ruby實(shí)現出現的很早。但由于某些原因,YAML的Ruby實(shí)現是不嚴格的。隨著(zhù)其他語(yǔ)言的YAML實(shí)現慢慢出現,這個(gè)問(wèn)題日益明顯了。
畢竟每個(gè)實(shí)現語(yǔ)言的類(lèi)型不一樣的,不同語(yǔ)言文化也不同。因此不同實(shí)現的YAML的交互是個(gè)問(wèn)題。兼容性需要YAML規范來(lái)保證。而具體的實(shí)施還有很長(cháng)的道路要走。
但YAML在單一語(yǔ)言中,YAML的應用是沒(méi)有問(wèn)題的。
YAML在Ruby On Rails中經(jīng)常用作配置文件。比如數據庫信息的配置:
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'文件。
由于實(shí)現簡(jiǎn)單,解析成本很低,YAML特別適合在腳本語(yǔ)言中使用。列一下現有的語(yǔ)言實(shí)現:Ruby,Java,Perl,Python,PHP,OCaml,JavaScript。除了Java,其他都是腳本語(yǔ)言.
YAML比較適合做序列化。因為它是宿主語(yǔ)言數據類(lèi)型直轉的。
YAML做配置文件也不錯。比如Ruby on Rails的配置就選用的YAML。對ROR而言,這很自然,也很省事.
由于兼容性問(wèn)題,不同語(yǔ)言間的數據流轉建議現在不要用YAML.
無(wú)論多么完美的事物,都需要有對立面,有說(shuō)“NO”的聲音。XML也不例外。當然,站在主流的對立面,需要勇氣和智慧。
YAML和XML不同,沒(méi)有自己的數據類(lèi)型的定義,而是使用實(shí)現語(yǔ)言的數據類(lèi)型。這一點(diǎn),有可能是出奇制勝的地方,也可能是一個(gè)敗筆。如果兼容性保證的不好的話(huà),YAML數據在不同語(yǔ)言間流轉會(huì )有問(wèn)題。如果兼容性好的話(huà),YAML就會(huì )成為不同語(yǔ)言間數據流通的橋梁。建議yaml.org設立兼容認證機制,每個(gè)語(yǔ)言的實(shí)現必須通過(guò)認證。
假如兼容性沒(méi)問(wèn)題的話(huà),YAML就太完美了。輕巧,敏捷,高效,簡(jiǎn)便,通用。這才是理想中的數據模型。當然就現在而言,這還只是個(gè)理想。
聯(lián)系客服