可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have the following struct:
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"`
}
And I use the encoding/xml
to encode this and then display it on web page.
The ProductName
field needs to be enclosed with <![CDATA[]]
. But if I write it as <![CDATA[ + p.ProductName + ]]>
, the <
and >
will be translated to <
and >
.
How can I create the CDATA
at minimal cost?
回答1:
As @Tomalak mentioned, outputting CDATA is not supported.
You can probably write ![CDATA[
as xml tag and later on replace the closing tag from the resulting xml. Will this work for you? Its probably not the one with minimal costs, but easiest. You can of course replace the MarshalIndent call with just the Marshal call in the example below.
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))
}
回答2:
@spirit-zhang: since Go 1.6, you can now use ,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))
}
Outputs:
<root>
<summary><![CDATA[<a href="http://example.org">My Example Website</a>]]></summary>
</root>
Playground: https://play.golang.org/p/xRn6fe0ilj
The rules are basically: 1) it has to be ,cdata
, you can't specify the node name and 2) use the xml.Name
to name the node as you want.
This is how most of the custom stuff for Go 1.6+ and XML works these days (embedded structs with xml.Name
).
EDIT: Added xml:"summary"
to the RootElement
struct, so you can you can also Unmarshal
the xml back to the struct in reverse (required to be set in both places).
回答3:
I'm not sure which version of go the innerxml tag became available in, but it allows you to include data which won't be escaped:
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¶m2=bar")
s.Escaped = "http://www.example.com/?param1=foo¶m2=bar"
data, _ := xml.MarshalIndent(s, "", "\t")
os.Stdout.Write(data)
}
Output:
<SomeXML>
<Unescaped><![CDATA[http://www.example.com/?param1=foo¶m2=bar]]></Unescaped>
<Escaped>http://www.example.com/?param1=foo&param2=bar</Escaped>
</SomeXML>
回答4:
Expanding on the answer by @BeMasher, you can use the xml.Marshaller
interface to do the work for you.
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¶m2=bar"
s.Escaped = "http://www.example.com/?param1=foo¶m2=bar"
data, _ := xml.MarshalIndent(s, "", "\t")
os.Stdout.Write(data)
}
Output:
<SomeXML>
<Unescaped><![CDATA[http://www.example.com/?param1=foo¶m2=bar]]></Unescaped>
<Escaped>http://www.example.com/?param1=foo&param2=bar</Escaped>
</SomeXML>
回答5:
If you use Go version 1.6 or later, just adding 'cdata' tag will work fine.
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"`
}
回答6:
CDATA with ",cdata" notation. It is handy to create struct with "Cdata" and use along with your xml object
package main
import (
"encoding/xml"
"fmt"
)
type Person struct {
Name string `xml:"Name"`
Age int `xml:"AGE"`
Address Cdata `xml:"ADDRESS"`
}
type Cdata struct {
Value string `xml:",cdata"`
}
func main() {
var address Cdata
address.Value = "John's House, <House #>: 10,Universe PIN: 00000