2013-01-07 30 views
11

我有以下結構:如何用go去創建xml的CDATA節點?

type XMLProduct struct { 
    XMLName   xml.Name `xml:"row"` 
    ProductId  string `xml:"product_id"` 
    ProductName  string `xml:"product_name"` 
    OriginalPrice string `xml:"original_price"` 
    BargainPrice  string `xml:"bargain_price"` 
    TotalReviewCount int  `xml:"total_review_count"` 
    AverageScore  float64 `xml:"average_score"` 
} 

,而且我用encoding/xml編碼本,然後在網頁上顯示。

ProductName字段需要用<![CDATA[]]括起來。但如果我將它寫爲<![CDATA[ + p.ProductName + ]]>,則<>將被翻譯爲&lt;&gt;

如何以最低的成本創建CDATA

+0

它爲什麼需要*成爲CDATA? CDATA部分是一種便利設施,它可以與XML編碼值互換,並且文檔將相同。 – Tomalak

+3

@Tomalak這是公司的規範... –

+0

[encoding/xml/marshal.go'的源代碼](http://golang.org/src/pkg/encoding/xml/marshal.go)並不表示輸出CDATA被支持。 *(同樣,CDATA在技術上是不必要的,也許規格可以修改呢?)* – Tomalak

回答

3

正如@Tomalak所述,不支持輸出CDATA。

您可能會將![CDATA[寫爲xml標記,稍後將從結果xml中替換結束標記。這會爲你工作嗎?它可能不是成本最低的那個,但最容易。你當然可以在下面的例子中用MarshalInc調用來代替MarshalIndent調用。

http://play.golang.org/p/2-u7H85-wn

package main 

import (
    "encoding/xml" 
    "fmt" 
    "bytes" 
) 

type XMLProduct struct { 
    XMLName   xml.Name `xml:"row"` 
    ProductId  string `xml:"product_id"` 
    ProductName  string `xml:"![CDATA["` 
    OriginalPrice string `xml:"original_price"` 
    BargainPrice  string `xml:"bargain_price"` 
    TotalReviewCount int  `xml:"total_review_count"` 
    AverageScore  float64 `xml:"average_score"` 
} 

func main() { 
    prod := XMLProduct{ 
     ProductId:  "ProductId", 
     ProductName:  "ProductName", 
     OriginalPrice: "OriginalPrice", 
     BargainPrice:  "BargainPrice", 
     TotalReviewCount: 20, 
     AverageScore:  2.1} 

    out, err := xml.MarshalIndent(prod, " ", " ") 
    if err != nil { 
     fmt.Printf("error: %v", err) 
     return 
    } 

    out = bytes.Replace(out, []byte("<![CDATA[>"), []byte("<![CDATA["), -1) 
    out = bytes.Replace(out, []byte("</![CDATA[>"), []byte("]]>"), -1) 
    fmt.Println(string(out)) 
} 
+7

這太可怕了,很傷心。是否有人提交了一個增強請求,以獲得更高效的實現到標準api中? –

+0

@ Rick-777:如果對功能有合理的需求,或許。但正如其他評論所說,XML解析器需要將CDATA塊和等效編碼字符數據視爲相同,所以沒有太多理由關心編碼時使用哪個版本。 –

+2

這不完全正確。解析器需要查找CDATA的結尾,但不能解析塊中的所有字符數據。這意味着它可以很容易地將包含< and >符號的逐字JavaScript代碼放入XHTML中,而無需使用<或>表單。 –

5

我不知道去innerxml標籤成爲可用的版本,但它可以讓你包括不會被轉義的數據:

代碼:

package main 

import (
    "encoding/xml" 
    "os" 
) 

type SomeXML struct { 
    Unescaped CharData 
    Escaped string 
} 

type CharData struct { 
    Text []byte `xml:",innerxml"` 
} 

func NewCharData(s string) CharData { 
    return CharData{[]byte("<![CDATA[" + s + "]]>")} 
} 

func main() { 
    var s SomeXML 
    s.Unescaped = NewCharData("http://www.example.com/?param1=foo&param2=bar") 
    s.Escaped = "http://www.example.com/?param1=foo&param2=bar" 
    data, _ := xml.MarshalIndent(s, "", "\t") 
    os.Stdout.Write(data) 
} 

輸出:

<SomeXML> 
    <Unescaped><![CDATA[http://www.example.com/?param1=foo&param2=bar]]></Unescaped> 
    <Escaped>http://www.example.com/?param1=foo&amp;param2=bar</Escaped> 
</SomeXML> 
1

通過擴展@BeMasher的答案,您可以使用xml.Marshaller界面爲您完成工作。

package main 

import (
    "encoding/xml" 
    "os" 
) 

type SomeXML struct { 
    Unescaped CharData 
    Escaped string 
} 

type CharData string 

func (n CharData) MarshalXML(e *xml.Encoder, start xml.StartElement) error { 
    return e.EncodeElement(struct{ 
     S string `xml:",innerxml"` 
    }{ 
     S: "<![CDATA[" + string(n) + "]]>", 
    }, start) 
} 

func main() { 
    var s SomeXML 
    s.Unescaped = "http://www.example.com/?param1=foo&param2=bar" 
    s.Escaped = "http://www.example.com/?param1=foo&param2=bar" 
    data, _ := xml.MarshalIndent(s, "", "\t") 
    os.Stdout.Write(data) 
} 

輸出:

<SomeXML> 
    <Unescaped><![CDATA[http://www.example.com/?param1=foo&param2=bar]]></Unescaped> 
    <Escaped>http://www.example.com/?param1=foo&amp;param2=bar</Escaped> 
</SomeXML> 
1

如果您使用Go 1.6或更高版本,只需添加 'CDATA' 標籤將正常工作。

type XMLProduct struct { 
    XMLName   xml.Name `xml:"row"` 
    ProductId  string `xml:"product_id"` 
    ProductName  string `xml:"product_name,cdata"` 
    OriginalPrice string `xml:"original_price"` 
    BargainPrice  string `xml:"bargain_price"` 
    TotalReviewCount int  `xml:"total_review_count"` 
    AverageScore  float64 `xml:"average_score"` 
} 
+0

'[] xml:字段ProductName中的無效標籤main.XMLProduct: 「product_name,cdata」' – Bryce

4

@精神章:自走1.6,現在可以使用,cdata標籤:

package main 

import (
    "fmt" 
    "encoding/xml" 
) 

type RootElement struct { 
    XMLName xml.Name `xml:"root"` 
    Summary *Summary `xml:"summary"` 
} 

type Summary struct { 
    XMLName xml.Name `xml:"summary"` 
    Text string `xml:",cdata"` 
} 

func main() { 

    cdata := `<a href="http://example.org">My Example Website</a>` 
    v := RootElement{ 
     Summary: &Summary{ 
      Text: cdata, 
     }, 
    } 

    b, err := xml.MarshalIndent(v, "", " ") 
    if err != nil { 
     fmt.Println("oopsie:", err) 
     return 
    } 
    fmt.Println(string(b)) 
} 

輸出:

<root> 
    <summary><![CDATA[<a href="http://example.org">My Example Website</a>]]></summary> 
</root> 

遊樂場:https://play.golang.org/p/xRn6fe0ilj

的規則基本上是:1)它必須是,cdata,你不能指定節點名稱和2)根據需要使用xml.Name來命名節點。

這就是爲什麼大部分Go 1.6+和XML的自定義內容現在都是如此工作的(嵌入式結構與xml.Name)。


編輯:添加xml:"summary"RootElement結構,所以你可以你也可以Unmarshal的XML回反向的結構(需要在兩個地方進行設置)。

+1

這個答案適合我。 – Melvin

+0

編輯:增加了Unmarshal xml返回結構的能力(缺少'xml'標籤) – eduncan911