如何在Golang中将自定义类型(字符串)编组为CDATA格式?

huangapple go评论94阅读模式
英文:

How to make custom type (string) marshal CDATA format in 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>

你有什么好的建议吗?谢谢。

英文:

WeChat message reply requires such a format, CDATA is to resolve the special characters.

&lt;xml&gt;
&lt;ToUserName&gt;&lt;![CDATA[toUser]]&gt;&lt;/ToUserName&gt;
&lt;FromUserName&gt;&lt;![CDATA[fromUser]]&gt;&lt;/FromUserName&gt;
&lt;CreateTime&gt;12345678&lt;/CreateTime&gt;
&lt;MsgType&gt;&lt;![CDATA[text]]&gt;&lt;/MsgType&gt;
&lt;Content&gt;&lt;![CDATA[hello world]]&gt;&lt;/Content&gt;
&lt;/xml&gt;

When using golang to achieve the specification,I found that the xml.Marshal () can be used with the struct tag xml:&quot;,cdata&quot; .
Define a struct to deal with, the codes seems like:

package main

import (
    &quot;encoding/xml&quot;
    &quot;fmt&quot;
    &quot;time&quot;
)

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

type CDATA struct {
    Text string `xml:&quot;,cdata&quot;`
}

func main() {
    msg := TextMsg{
        ToUserName:   CDATA{&quot;userId&quot;},
        FromUserName: CDATA{&quot;appId&quot;},
        CreateTime:   time.Now().Unix(),
        MsgType:      CDATA{&quot;text&quot;},
        Content:      CDATA{&quot;some message like &lt;hello&gt;&quot;}}

    b, _ := xml.MarshalIndent(msg, &quot;&quot;, &quot;    &quot;)
    fmt.Println(string(b))
}

> Output results:

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

But I think it's not perfect, since variable assignment is not as convenient as the normal string type, so I change the CDATA into string type, and try to achieve MarshalXML ():

package main

import (
    &quot;encoding/xml&quot;
    &quot;fmt&quot;
    &quot;time&quot;
)

type TextMsg struct {
    XMLName      xml.Name `xml:&quot;xml&quot;`
    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(&quot;&lt;![CDATA[&quot;+string(c)+&quot;]]&gt;&quot;, start)
    return nil
}

func main() {
    msg := TextMsg{
        ToUserName:   &quot;userId&quot;,
        FromUserName: &quot;appId&quot;,
        CreateTime:   time.Now().Unix(),
        MsgType:      &quot;text&quot;,
        Content:      &quot;some message like &lt;hello&gt;&quot;}

    b, _ := xml.MarshalIndent(msg, &quot;&quot;, &quot;    &quot;)
    fmt.Println(string(b))
}

> But the output results do not meet expectations, "<" or ">" is
> escaped:

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

Do you have any good suggestions for me, thank you.

答案1

得分: 1

你可以创建另一个名为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中查看示例。

英文:

You can create another structure CDATA2 which has tag xml:&quot;,cdata&quot;, and pass it to EncodeElement().

EncodeElement() would correctly encode CDATA2{&quot;foo&lt;&gt;&quot;} to &lt;![CDATA[foo&lt;&gt;]]&gt;.

type CDATA2 struct {
	Text string `xml:&quot;,cdata&quot;`
}

func (c CDATA) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
	e.EncodeElement(CDATA2{string(c)}, start)
	return nil
}

Check it in: Go Playground

Edit: you can use anonymous struct if you don't want to define named type

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

Check it in: Go Playground

huangapple
  • 本文由 发表于 2017年1月31日 14:52:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/41951345.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定