2013-01-07 7 views
11

Ich habe folgende Struktur:Wie erstellt man einen CDATA-Knoten von xml mit go?

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"` 
} 

Und ich benutze die encoding/xml diese zu kodieren und dann auf der Webseite angezeigt werden soll.

Das Feld ProductName muss mit <![CDATA[]] ausgefüllt werden. Aber wenn ich es als <![CDATA[ + p.ProductName + ]]> schreibe, werden die < und > zu &lt; und &gt; übersetzt. Wie kann ich CDATA zu minimalen Kosten erstellen?

+0

Warum muss * CDATA * sein? Ein CDATA-Abschnitt ist eine bequeme Einrichtung, er kann mit einem XML-codierten Wert ausgetauscht werden, und das Dokument wäre dasselbe. – Tomalak

+3

@Tomalak Es ist die Firmenspezifikation ... –

+0

Die [Quelle von 'encoding/xml/marshal.go'] (http://golang.org/src/pkg/encoding/xml/marshal.go) schlägt das nicht vor Ausgabe von CDATA wird unterstützt. * (Wiederum ist CDATA technisch unnötig. Vielleicht kann die Spezifikation doch geändert werden?) * – Tomalak

Antwort

3

Wie bei @Tomalak erwähnt, wird die Ausgabe von CDATA nicht unterstützt.

Sie können wahrscheinlich ![CDATA[ als XML-Tag schreiben und später das schließende Tag aus dem resultierenden XML ersetzen. Wird das für dich funktionieren? Es ist wahrscheinlich nicht der mit minimalen Kosten, aber am einfachsten. Sie können den MarshalIndent-Aufruf natürlich nur durch den Marshal-Aufruf im folgenden Beispiel ersetzen.

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

Das ist schrecklich und ziemlich traurig. Hat jemand eine Verbesserungsanfrage eingereicht, um eine effizientere Implementierung in die Standard-API zu bekommen? –

+0

@ Rick-777: wenn es ein legitimes Bedürfnis für das Feature gab, vielleicht. Aber wie andere Kommentare gesagt haben, sind XML-Parser erforderlich, um CDATA-Blöcke und äquivalente codierte Zeichendaten gleich zu behandeln, so dass es keinen Grund gibt, sich darum zu kümmern, welche Version beim Codieren verwendet wird. –

+2

Das ist nicht ganz richtig. Der Parser muss das Ende der CDATA finden, aber ansonsten nicht alle Zeichendaten im Block analysieren. Dies bedeutet, dass es zum Beispiel einfach ist, wörtlichen JavaScript-Code mit < and > Symbolen in XHTML zu setzen, ohne das Formular <oder> verwenden zu müssen. –

5

Ich bin sicher nicht, welche Version gehen die InnerXml Tag in verfügbar wurde, aber es ermöglicht es Ihnen, Daten aufzunehmen, die nicht entgangen sein wird:

Code:

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) 
} 

Ausgang:

<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

Erweitern auf die Antwort von @BeMasher, können Sie die xml.Marshaller Schnittstelle verwenden, um die Arbeit für Sie zu erledigen.

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) 
} 

Ausgang:

<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

Wenn Sie Go Version verwenden 1.6 oder später, gerade hinzufügen 'cdata' Tag wird gut funktionieren.

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: ungültiges Tag im Feld ProductName des Typs main.XMLProduct: "product_name, cdata" ' – Bryce

4

@ Geist-zhang: seit Go 1.6 können Sie jetzt ,cdata Tags verwenden:

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)) 
} 

Ausgänge:

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

Spielplatz: https://play.golang.org/p/xRn6fe0ilj

Die Regeln sind im Grunde : 1) Es muss ,cdata sein, Sie können den Knotennamen nicht angeben und 2) Verwenden Sie die xml.Name, um den Knoten wie gewünscht zu benennen.

So funktioniert heute die meisten benutzerdefinierten Sachen für Go 1.6+ und XML (eingebettete Strukturen mit xml.Name).


EDIT: Hinzugefügt xml:"summary" zur RootElement Struktur, so können Sie auch Unmarshal die xml zurück auf die Struktur in umgekehrter Richtung (erforderlich in beiden Orten festgelegt werden).

+1

Diese Antwort funktionierte für mich. – Melvin

+0

EDIT: hinzugefügt die Fähigkeit zu Unmarshal Xml zurück in eine Struktur (fehlte ein 'xml' Tag) – eduncan911

Verwandte Themen