英文:
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><![CDATA[userId]]></ToUserName>
<FromUserName><![CDATA[appId]]></FromUserName>
<CreateTime>1485837470</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[some message like <hello>]]></Content>
</xml>
你有什么好的建议吗?谢谢。
英文:
WeChat message reply requires such a format, CDATA is to resolve the special characters.
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>12345678</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[hello world]]></Content>
</xml>
When using golang
to achieve the specification,I found that the xml.Marshal () can be used with the struct tag xml:",cdata"
.
Define a struct to deal with, the codes seems like:
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))
}
> Output results:
<xml>
<ToUserName><![CDATA[userId]]></ToUserName>
<FromUserName><![CDATA[appId]]></FromUserName>
<CreateTime>1485837083</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[some message like <hello>]]></Content>
</xml>
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 (
"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))
}
> But the output results do not meet expectations, "<" or ">" is
> escaped:
<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>
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:",cdata"
, and pass it to EncodeElement()
.
EncodeElement()
would correctly encode CDATA2{"foo<>"}
to <![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
}
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:",cdata"`
}{string(c)}, start)
return nil
}
Check it in: Go Playground
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论