如何在golang中自定义类型(字符串)元帅CDATA格式?

问题描述:

微信息回复需要这样的格式,CDATA是解析特殊字符。如何在golang中自定义类型(字符串)元帅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()可以与结构标签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> 

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

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

EncodeElement()将正确编码CDATA2{"foo<>"}<![CDATA[foo<>]]>

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

+0

非常感谢!这真是一个好主意。我更改了代码,以便不会导出额外的结构:'func(c CDATA)Mar shalXML(E * xml.Encoder,启动xml.StartElement)错误{ \t返回e.EncodeElement(结构{ \t \t字符串 “XML:\”,CDATA \ “” \t} {串(C)},启动) }' – woylyn

+0

@woylyn不客气。因为你的代码看起来更聪明。我会编辑答案。 – ymonad