
本文介绍如何在不定义预设结构体的前提下,使用 go 标准库将任意 json 字符串解析为通用接口后,动态序列化为 xml 格式,适用于配置转换、api 协议适配等场景。
本文介绍如何在不定义预设结构体的前提下,使用 go 标准库将任意 json 字符串解析为通用接口后,动态序列化为 xml 格式,适用于配置转换、api 协议适配等场景。
在 Go 中实现 JSON 到 XML 的动态转换,核心思路是:先通过 json.Unmarshal 将原始 JSON 解析为 interface{}(即 map[string]interface{} 或嵌套切片/基本类型),再借助 encoding/xml 包将其序列化为 XML 字节流。该方案完全避免硬编码 struct,适用于未知 schema 的灵活场景。
✅ 基础实现步骤
- 导入必要包:encoding/json 和 encoding/xml;
- 解析 JSON 字符串:使用 json.Unmarshal 转为 interface{};
- 序列化为 XML:调用 xml.Marshal,注意传入指针以确保嵌套结构正确处理。
以下是完整可运行示例:
package main
import (
"encoding/json"
"encoding/xml"
"fmt"
"log"
)
func main() {
jsonStr := `{
"name": "Alice",
"age": 30,
"tags": ["golang", "xml", "json"],
"address": {
"city": "Beijing",
"zip": "100000"
}
}`
var data interface{}
if err := json.Unmarshal([]byte(jsonStr), &data); err != nil {
log.Fatal("JSON unmarshal failed:", err)
}
xmlBytes, err := xml.MarshalIndent(data, "", " ")
if err != nil {
log.Fatal("XML marshal failed:", err)
}
fmt.Println(string(xmlBytes))
}
输出示例:
<map><name>Alice</name><age>30</age><tags>golang</tags><tags>xml</tags><tags>json</tags><address>
<city>Beijing</city><zip>100000</zip>
</address>
</map>
⚠️ 注意:Go 的 xml.Marshal 对 interface{} 的默认行为有明确约定:
- map[string]interface{} → 顶层
- []interface{} → 每个元素生成同名标签(如 tags → 多个
); - 基本类型(string/int/bool)→ 直接作为文本内容;
- nil 值会被忽略,空 map/slice 生成空标签。
? 关键限制与注意事项
-
无字段重命名能力:标准 xml.Marshal 不支持为 interface{} 中的 key 自定义 XML 标签名(如将 "name" 映射为
),需自行封装映射逻辑或改用第三方库(如 github.com/bmizerany/assert 风格的中间转换层); - 不支持 XML 属性:所有数据均作为元素内容或子元素,无法生成 id="123" 类属性;
- 类型推断局限性:JSON 数字统一转为 float64,若需精确整型语义,应在 Unmarshal 后做类型断言或使用 json.RawMessage 预处理;
-
根元素固定为 :如需自定义根标签(如
),可包装一层结构体或使用 xml.Header + 字符串替换(不推荐生产环境)。
✅ 推荐增强实践(轻量级封装)
若需更可控的输出,可添加简易包装器:
type XMLRoot struct {
XMLName xml.Name `xml:"user"`
Data interface{} `xml:",any"`
}
// 使用时:
root := XMLRoot{Data: data}
xmlBytes, _ := xml.MarshalIndent(root, "", " ")
这能将根元素从
总之,Go 标准库已提供开箱即用的 JSON→XML 动态转换能力,关键在于理解 interface{} 在 encoding/xml 中的序列化规则。合理利用 xml.MarshalIndent 和类型安全的解包逻辑,即可在微服务网关、日志格式桥接、遗留系统集成等场景中高效落地。











