2013-01-07 21 views
11

Ho il seguente struct:Come creare un nodo CDATA di xml con 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"` 
} 

e io uso il encoding/xml per codificare questo e poi visualizzarlo sulla pagina web.

Il campo ProductName deve essere chiuso con <![CDATA[]]. Ma se lo scrivo come <![CDATA[ + p.ProductName + ]]>, il < e > sarà tradotto in &lt; e &gt;.

Come posso creare il CDATA al minimo costo?

+0

Perché * deve * essere CDATA? Una sezione CDATA è una comodità, può essere interscambiata con un valore codificato XML e il documento sarebbe lo stesso. – Tomalak

+3

@Tomalak È la specifica dell'azienda ... –

+0

La [fonte di 'encoding/xml/marshal.go'] (http://golang.org/src/pkg/encoding/xml/marshal.go) non suggerisce che l'emissione di CDATA è supportata. * (Anche in questo caso, CDATA non è tecnicamente necessario Forse la specifica può essere modificata dopo tutto?) * – Tomalak

risposta

3

Come menzionato da @Tomalak, l'emissione di CDATA non è supportata.

Probabilmente è possibile scrivere ![CDATA[ come tag xml e in seguito sostituire il tag di chiusura dall'XML risultante. Funzionerà per te? Probabilmente non è quello con costi minimi, ma più semplice. Ovviamente è possibile sostituire la chiamata MarshalIndent con la sola chiamata Marshal nell'esempio seguente.

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

È orribile e piuttosto triste. Qualcuno ha presentato una richiesta di miglioramento per ottenere un'implementazione più efficiente nell'API standard? –

+0

@ Rick-777: se c'era una necessità legittima per la funzionalità, forse. Ma come altri commenti hanno detto, i parser XML sono necessari per trattare i blocchi CDATA e i dati di caratteri codificati equivalenti allo stesso modo, quindi non ci sono molti motivi per preoccuparsi di quale versione viene usata durante la codifica. –

+2

Non è del tutto corretto. Il parser è necessario per trovare la fine del CDATA ma non altrimenti analizzare tutti i dati dei caratteri nel blocco. Ciò significa che è, ad esempio, facile inserire il codice javascript verbatim contenente i simboli < and > in XHTML senza utilizzare il modulo <o>. –

5

non sono sicuro quale versione di andare il tag InnerXml si sono resi disponibili a, ma permette di includere i dati che non saranno sfuggiti:

Codice:

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

uscita:

<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

Espandendo la risposta di @BeMasher, è possibile utilizzare l'interfaccia xml.Marshaller per eseguire il lavoro.

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

uscita:

<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

Se si utilizza Go versione 1.6 o successiva, solo l'aggiunta di tag 'CDATA' funzionerà bene.

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: tag non valido nel campo ProductName di tipo main.XMLProduct: "product_name, cdata" ' – Bryce

4

@ spirito-Zhang: dal Go 1.6, è ora possibile utilizzare ,cdata tags:

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

Uscite:

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

Parco giochi https://play.golang.org/p/xRn6fe0ilj

Le regole sono fondamentalmente : 1) deve essere ,cdata, non è possibile specificare il nome del nodo e 2) utilizzare xml.Name per denominare il nodo come si desidera.

Questo è il modo in cui la maggior parte delle cose personalizzate per Go 1.6+ e XML funziona in questi giorni (strutture incorporate con xml.Name).


EDIT: Aggiunto xml:"summary" al RootElement struct, in modo da poter potete anche Unmarshal xml torna alla struct in senso inverso (richiesto da impostare in entrambi i posti).

+1

Questa risposta ha funzionato per me. – Melvin

+0

EDIT: aggiunta la possibilità di Unmarshal xml di ritornare in una struct (mancava un tag 'xml') – eduncan911