2013-01-07 19 views
11

Mam następujące struct:Jak utworzyć węzeł CDATA z xml za pomocą 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"` 
} 

i używam encoding/xml zakodować to, a następnie wyświetlić go na stronie internetowej.

Pole ProductName należy dołączyć do <![CDATA[]]. Ale jeśli napiszę to jako <![CDATA[ + p.ProductName + ]]>, < i > zostaną przetłumaczone na &lt; i &gt;.

Jak mogę utworzyć CDATA przy minimalnych kosztach?

+0

Dlaczego * potrzeba * być CDATA? Sekcja CDATA jest udogodnieniem, może być wymieniana z zakodowaną wartością XML, a dokument będzie taki sam. – Tomalak

+3

@ Tomalak Jest to specyfikacja firmy ... –

+0

Źródło [kodowanie/xml/marshal.go'] (http://golang.org/src/pkg/encoding/xml/marshal.go) nie sugeruje, że wyprowadzanie CDATA jest obsługiwane. * (Ponownie, CDATA jest technicznie niepotrzebna, może specyfikacja może być zmodyfikowana?) * – Tomalak

Odpowiedz

3

Jak wspomniano w @Tomalak, wyprowadzanie CDATA nie jest obsługiwane.

Prawdopodobnie można napisać ![CDATA[ jako znacznik xml, a następnie zastąpić znacznik zamykający z wynikowego xml. Czy to zadziała dla Ciebie? Prawdopodobnie nie jest to koszt minimalny, ale najłatwiejszy. Oczywiście można zastąpić wywołanie MarshalIndent tylko za pomocą wywołania Marshal w poniższym przykładzie.

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

To okropne i dość smutne. Czy ktoś zgłosił prośbę o ulepszenie, aby uzyskać bardziej wydajną implementację w standardowym interfejsie API? –

+0

@ Rick-777: jeśli istniało uzasadnione zapotrzebowanie na tę funkcję. Ale, jak powiedzieli inni komentatorzy, parsery XML są wymagane, aby traktować bloki CDATA i równoważne zakodowane dane znakowe takie same, więc nie ma powodu, by obchodzić, która wersja jest używana podczas kodowania. –

+2

To nie do końca poprawne. Analizator składni jest wymagany do znalezienia końca CDATA, ale w przeciwnym razie nie przeanalizuje wszystkich danych znakowych w bloku. Oznacza to, że na przykład łatwe jest umieszczenie dosłownego kodu javascript zawierającego symbole < and > w XHTML bez konieczności używania formularza <lub>. –

5

Nie jestem pewien, która wersja iść tag innerxml stały się dostępne w, ale pozwala na to dane, które nie będzie ucieczki:

Kod:

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

wyjściowa:

<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

Po rozwinięciu odpowiedzi przez @BeMasher możesz skorzystać z interfejsu xml.Marshaller, aby wykonać pracę za Ciebie.

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

wyjściowa:

<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

Jeśli używasz Go w wersji 1.6 lub nowszej, wystarczy dodać tag 'CDATA' będzie działać dobrze.

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: invalid tag w polu ProductName typu main.XMLProduct: "product_name, cdata" ' – Bryce

4

@ duchem-Zhang: ponieważ Go 1.6, można teraz używać ,cdata tagi:

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

Wyjścia:

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

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

Zasady są w zasadzie : 1) musi to być ,cdata, nie można podać nazwy węzła i 2) użyj nazwy xml.Name, aby nazwać węzeł, jak chcesz.

W ten sposób większość niestandardowych rzeczy dla Go 1.6+ i XML działa obecnie (wbudowane struktury z xml.Name).


EDIT: Dodano xml:"summary" do RootElement struktury, więc może można również Unmarshal xml z powrotem do struktury w odwrotnej kolejności (muszą być ustawione w obu miejscach).

+1

Ta odpowiedź zadziałała. – Melvin

+0

EDYCJA: dodano możliwość cofnięcia Unmarshal xml do struktury (brakowało znacznika 'xml') – eduncan911