【问题标题】:How to make custom type (string) marshal CDATA format in golang?如何在 golang 中制作自定义类型(字符串)编组 CDATA 格式?
【发布时间】:2017-01-31 06:52:36
【问题描述】:

微信回复需要这样的格式,CDATA是解析特殊字符的。

<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>12345678</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[hello world]]></Content>
</xml>

在使用golang实现规范时,发现xml.Marshal()可以和struct标签xml:",cdata"一起使用。 定义一个结构体来处理,代码如下:

package main

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

type TextMsg struct {
    XMLName      xml.Name `xml:"xml"`
    ToUserName   CDATA
    FromUserName CDATA
    CreateTime   int64
    MsgType      CDATA
    Content      CDATA
}

type CDATA struct {
    Text string `xml:",cdata"`
}

func main() {
    msg := TextMsg{
        ToUserName:   CDATA{"userId"},
        FromUserName: CDATA{"appId"},
        CreateTime:   time.Now().Unix(),
        MsgType:      CDATA{"text"},
        Content:      CDATA{"some message like <hello>"}}

    b, _ := xml.MarshalIndent(msg, "", "    ")
    fmt.Println(string(b))
}

输出结果:

<xml>
    <ToUserName><![CDATA[userId]]></ToUserName>
    <FromUserName><![CDATA[appId]]></FromUserName>
    <CreateTime>1485837083</CreateTime>
    <MsgType><![CDATA[text]]></MsgType>
    <Content><![CDATA[some message like <hello>]]></Content>
</xml>

但我觉得不完美,因为变量赋值不如普通字符串类型方便,所以我把CDATA改成字符串类型,尝试实现MarshalXML():

package main

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

type TextMsg struct {
    XMLName      xml.Name `xml:"xml"`
    ToUserName   CDATA
    FromUserName CDATA
    CreateTime   int64
    MsgType      CDATA
    Content      CDATA
}

type CDATA string

func (c CDATA) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
    e.EncodeElement("<![CDATA["+string(c)+"]]>", start)
    return nil
}

func main() {
    msg := TextMsg{
        ToUserName:   "userId",
        FromUserName: "appId",
        CreateTime:   time.Now().Unix(),
        MsgType:      "text",
        Content:      "some message like <hello>"}

    b, _ := xml.MarshalIndent(msg, "", "    ")
    fmt.Println(string(b))
}

但输出结果不符合预期,“”是 转义:

<xml>
    <ToUserName>&lt;![CDATA[userId]]&gt;</ToUserName>
    <FromUserName>&lt;![CDATA[appId]]&gt;</FromUserName>
    <CreateTime>1485837470</CreateTime>
    <MsgType>&lt;![CDATA[text]]&gt;</MsgType>
    <Content>&lt;![CDATA[some message like &lt;hello&gt;]]&gt;</Content>
</xml>

你有什么好的建议给我吗,谢谢。

【问题讨论】:

    标签: xml go cdata


    【解决方案1】:

    您可以创建另一个结构CDATA2,其标签为xml:",cdata",并将其传递给EncodeElement()

    EncodeElement() 会正确地将CDATA2{"foo&lt;&gt;"} 编码为&lt;![CDATA[foo&lt;&gt;]]&gt;

    type CDATA2 struct {
        Text string `xml:",cdata"`
    }
    
    func (c CDATA) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
        e.EncodeElement(CDATA2{string(c)}, start)
        return nil
    }
    

    签到:Go Playground

    编辑:如果不想定义命名类型,可以使用匿名结构

    func (c CDATA) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
        e.EncodeElement(struct {
            string `xml:",cdata"`
        }{string(c)}, start)
        return nil
    }
    

    签到:Go Playground

    【讨论】:

    • 非常感谢!真是个好主意。我改了代码让多余的结构不会被导出:func (c CDATA) MarshalXML(e *xml.Encoder, start xml.StartElement) error { return e.EncodeElement(struct { string "xml:\",cdata\"" }{string(c)}, start) }
    • @woylyn 不客气。因为你的代码看起来更聪明。我会编辑答案。
    猜你喜欢
    • 1970-01-01
    • 2011-12-31
    • 2011-01-04
    • 1970-01-01
    • 1970-01-01
    • 2019-04-19
    • 1970-01-01
    • 1970-01-01
    • 2012-08-28
    相关资源
    最近更新 更多