通過Go來分析和建立XML

go_9發表於2019-01-26

一、分析 XML

下面有一個 xml 檔案讓你分析。

<?xml version="1.0" encoding="utf-8"?>
<post id="1">
    <content>Hello World!</content>
    <author id="2">Sau She</author>
    <comments>
        <comment id="1">
            <content>Have a great day!</content>
            <author id="3">Adam</author>
        </comment>
        <comment id="2">
            <content>How are you today?</content>
            <author id="4">Betty</author>
        </comment>
    </comments>
</post>

怎麼樣去分析呢?我們要遵循一下原則。


(1)通過建立一個名為 XMLName、型別為 xml.Name 的欄位,可以將 XML 元素的名字儲存在這個欄位裡面。

(2)通過建立一個與 XML 元素屬性同名的欄位,並使用 xml:"<name>,attr"作為該欄位的結構標籤,可以將元素的屬性的值儲存到這個欄位裡面。

(3)通過建立一個與 XML 元素相同的欄位,並使用 xml:",chardata"作為該欄位的結構標籤,可以將 XML 元素的字元資料儲存到這個欄位裡面。

(4)通過定義一個任意名字欄位,並使用 XML:",innerxml"作為該欄位的結構標籤,可以將 XML 元素中的原始 XML 儲存到這個欄位裡面。

(5)沒有模式標籤(如,attr、,chardata 或者,innerxml)的結構欄位將與同名的 XML 元素匹配。

(6)使用 xml:"a>b>c"這樣的結構標籤可以在不指定樹樁結構的情況下直接獲取指定的 XML 元素,其中 a 和 b 為中間元素,而 c 則是想要獲取的節點元素。


*解析程式碼 *

package main

import (
    &quot;encoding/xml&quot;
    &quot;fmt&quot;
    &quot;io/ioutil&quot;
    &quot;log&quot;
    &quot;os&quot;
)

type Post struct {
    XMLName xml.Name `xml:&quot;post&quot;`
    Id string `xml:&quot;id,attr&quot;`
    Author Author `xml:&quot;author&quot;`
    Xml string `xml:&quot;,innerxml&quot;`
    Comments []Comment `xml:&quot;comments&gt;comment&quot;`
}

type Author struct {
    Id  string `xml:&quot;id,attr&quot;`
    Name string `xml:&quot;,chardata&quot;`
}

type Comment struct {
    Id string `xml:&quot;id,attr&quot;`
    Content string `xml:&quot;content&quot;`
    Author Author `xml:&quot;author&quot;`
}

func main() {
    file, err := os.Open(&quot;src/text/xml/post.xml&quot;)
    if err != nil {
        log.Fatalf(&quot;Error opening XML file %v&quot;,err)
        return
    }
    defer file.Close()
    bytes, err := ioutil.ReadAll(file)
    if err != nil {
        log.Fatalf(&quot;Error reading XML file %v&quot;,err)
        return
    }
    var post Post
    xml.Unmarshal(bytes,&amp;post)
    fmt.Println(post)
}

上面的這種方法可以很好的處理體積較小的 XML 檔案,但是卻無法高效地處理以流方式傳輸的 XML 檔案以及體積較大的 XML 檔案。為了解決這個問題,我們需要使用 Decoder 結構來替代 Unmarshal 函式,通過手動解碼 XML 元素的方式來解封 XML 資料。

package main

import (
    &quot;encoding/xml&quot;
    &quot;fmt&quot;
    &quot;io&quot;
    &quot;log&quot;
    &quot;os&quot;
)

type Post struct {
    XMLName xml.Name `xml:&quot;post&quot;`
    Id string `xml:&quot;id,attr&quot;`
    Author Author `xml:&quot;author&quot;`
    Xml string `xml:&quot;,innerxml&quot;`
    Comments []Comment `xml:&quot;comments&gt;comment&quot;`
}

type Author struct {
    Id  string `xml:&quot;id,attr&quot;`
    Name string `xml:&quot;,chardata&quot;`
}

type Comment struct {
    Id string `xml:&quot;id,attr&quot;`
    Content string `xml:&quot;content&quot;`
    Author Author `xml:&quot;author&quot;`
}

func main() {
    file, err := os.Open(&quot;src/text/xml/post.xml&quot;)
    if err != nil {
        log.Fatalf(&quot;Error opening XML file %v&quot;,err)
        return
    }
    defer file.Close()
    decoder := xml.NewDecoder(file)  //根據給定的XML資料生成相應的解碼器
    for  {
        t, err := decoder.Token()    //每進行一次迭代,就從解碼器獲取一個token
        if err == io.EOF {
            break
        }
        if err != nil {
            log.Fatalf(&quot;Error decoding XML into tokens %v&quot;,err)
        }
        switch se := t.(type) {      //檢查token的型別
        case xml.StartElement:
            if se.Name.Local == &quot;comment&quot; {
                var comment Comment
                decoder.DecodeElement(&amp;comment,&amp;se)   //將XML資料解析至結構
                fmt.Println(comment)
            }
        }
    }
}

二、建立 XML

package main

import (
    &quot;encoding/xml&quot;
    &quot;io/ioutil&quot;
    &quot;log&quot;
)

type Post struct {
    XMLName xml.Name `xml:&quot;post&quot;`
    Id string `xml:&quot;id,attr&quot;`
    Content string `xml:&quot;content&quot;`
    Author Author `xml:&quot;author&quot;`
    Xml string `xml:&quot;,innerxml&quot;`
    Comments []Comment `xml:&quot;comments&gt;comment&quot;`
}

type Author struct {
    Id  string `xml:&quot;id,attr&quot;`
    Name string `xml:&quot;,chardata&quot;`
}

type Comment struct {
    Id string `xml:&quot;id,attr&quot;`
    Content string `xml:&quot;content&quot;`
    Author Author `xml:&quot;author&quot;`
}

func main() {
    post := Post{
        Id:&quot;1&quot;,
        Content:&quot;Hello World!&quot;,
        Author:Author{
            Id:&quot;2&quot;,
            Name:&quot;Sau Sheong&quot;,
        },
        Comments: []Comment{{
            Id:&quot;1&quot;,
            Content:&quot;i love sz&quot;,
            Author:Author{
                Id:&quot;3&quot;,
                Name:&quot;zhu&quot;,
            },
        }},
    }
    output, err := xml.MarshalIndent(&amp;post,&quot;&quot;,&quot;\t&quot;)   //生成段落式的XML
    if err != nil {
        log.Fatalf(&quot;Error marshalling to XML %v&quot;,err)
        return
    }
    err = ioutil.WriteFile(&quot;post2.xml&quot;, []byte(xml.Header+string(output)), 0644)  //新增XML宣告
    if err != nil {
        log.Fatalf(&quot;Error writing XML to file %v&quot;,err)
        return
    }
}

我們可以看一下生成的 post2.xml 檔案中的 xml 資料

&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;post id=&quot;1&quot;&gt;
    &lt;content&gt;Hello World!&lt;/content&gt;
    &lt;author id=&quot;2&quot;&gt;Sau Sheong&lt;/author&gt;
    &lt;comments&gt;
        &lt;comment id=&quot;1&quot;&gt;
            &lt;content&gt;i love sz&lt;/content&gt;
            &lt;author id=&quot;3&quot;&gt;zhu&lt;/author&gt;
        &lt;/comment&gt;
    &lt;/comments&gt;
&lt;/post&gt;

正如我們可以手動將 XML 解碼到 Go 結構裡面一樣,我們同樣也可以手動將 Go 結構編碼到 XML 裡面。

package main

import (
    &quot;encoding/xml&quot;
    &quot;log&quot;
    &quot;os&quot;
)

type Post struct {
    XMLName xml.Name `xml:&quot;post&quot;`
    Id string `xml:&quot;id,attr&quot;`
    Content string `xml:&quot;content&quot;`
    Author Author `xml:&quot;author&quot;`
    Xml string `xml:&quot;,innerxml&quot;`
    Comments []Comment `xml:&quot;comments&gt;comment&quot;`
}

type Author struct {
    Id  string `xml:&quot;id,attr&quot;`
    Name string `xml:&quot;,chardata&quot;`
}

type Comment struct {
    Id string `xml:&quot;id,attr&quot;`
    Content string `xml:&quot;content&quot;`
    Author Author `xml:&quot;author&quot;`
}

func main() {
    post := Post{
        Id:&quot;1&quot;,
        Content:&quot;Hello World!&quot;,
        Author:Author{
            Id:&quot;2&quot;,
            Name:&quot;Sau Sheong&quot;,
        },
        Comments: []Comment{{
            Id:&quot;1&quot;,
            Content:&quot;i love sz&quot;,
            Author:Author{
                Id:&quot;3&quot;,
                Name:&quot;zhu&quot;,
            },
        }},
    }
    xmlFile, err := os.Create(&quot;post3.xml&quot;)
    if err != nil {
        log.Fatalf(&quot;Error creating XML File %v&quot;,err)
        return
    }
    encoder := xml.NewEncoder(xmlFile)
    encoder.Indent(&quot;&quot;,&quot;\t&quot;)
    err = encoder.Encode(&amp;post)
    if err != nil {
        log.Fatalf(&quot;Error encoding XML to file %v&quot;,err)
        return
    }
}

更多原創文章乾貨分享,請關注公眾號
  • 通過Go來分析和建立XML
  • 加微信實戰群請加微信(註明:實戰群):gocnio

相關文章