本文讲解如何在Go中正确解析包含原始XML字符串字段的JSON响应,解决因XML内容含特殊字符(如
本文讲解如何在go中正确解析包含原始xml字符串字段的json响应,解决因xml内容含特殊字符(如`json导致的`invalid character '
在实际集成医疗、金融或政府类API(如CCDA、EPP、HL7等)时,常遇到一种典型场景:HTTP响应体是JSON格式,但其中某个字段(如 "ccda")的值是一个未经转义的、完整的XML文档字符串。此时若直接使用 json.Unmarshal 解析,极易触发 invalid character '
? 错误根源定位:先确认真实响应类型
首要步骤不是修改结构体,而是验证响应体是否真的是JSON:
body, err := io.ReadAll(resp.Body)
defer resp.Body.Close()
if err != nil {
log.Fatal("read body failed:", err)
}
// 打印前200字节(避免日志爆炸),观察真实内容
log.Printf("Raw response (first 200B): %q", string(body[:min(200, len(body))]))
// 检查Content-Type头
contentType := resp.Header.Get("Content-Type")
log.Printf("Content-Type: %s", contentType) // 若为 "application/xml" 或缺失,则非JSON!
若输出显示以 实际返回了XML而非JSON——这正是 json.Unmarshal 报错的根本原因。常见诱因包括:
- 请求未设置 Accept: application/json 头;
- URL 缺少 .json 后缀或 ?format=json 参数;
- 认证失败/Token过期,服务端静默降级返回XML错误页。
✅ 解决方案:强制指定请求头
req, _ := http.NewRequest("GET", url, nil)
req.Header.Set("Accept", "application/json") // 关键!
req.Header.Set("User-Agent", "Go-Client/1.0")
resp, err := http.DefaultClient.Do(req)
✅ 正确解析:结构体定义 + 容错反序列化
一旦确认响应为合法JSON(即 body 确实是 [{"ccda":"
type Ccda struct {
CCDA string `json:"ccda"`
}
var ccr []Ccda
err := json.Unmarshal(body, &ccr)
但需注意:XML字符串中的双引号、换行符、& 符号必须由服务端做JSON转义。例如合法JSON片段应为:
{
"ccda": "<?xml version=\"1.0\"?>\n<clinicaldocument xmlns='\"urn:hl7-org:v3\"'>..."
}</clinicaldocument>
而非:
{ "ccda": "<?xml version="1.0"?>..." } // ❌ 缺少转义,JSON非法
若服务端无法修复转义问题(如遗留系统),可采用“两阶段解析”绕过:
// Step 1: 先用 map[string]interface{} 解析顶层JSON,避免结构体约束
var raw map[string]interface{}
if err := json.Unmarshal(body, &raw); err != nil {
if syntaxErr, ok := err.(*json.SyntaxError); ok {
log.Printf("JSON syntax error at offset %d: %q",
syntaxErr.Offset,
string(body[max(0, int(syntaxErr.Offset)-20):min(len(body), int(syntaxErr.Offset)+20)]))
}
log.Fatal("Failed to parse JSON:", err)
}
// Step 2: 手动提取 ccda 字段(作为字符串)
ccdaBytes, ok := raw["ccda"].(string)
if !ok {
log.Fatal("ccda field is not a string")
}
// ccdaBytes 即为原始XML字符串,可直接转发
fmt.Printf("Extracted XML length: %d bytes", len(ccdaBytes))
⚠️ 注意事项与最佳实践
- 永远不要信任外部响应:对 Content-Type 和响应体做双重校验;
- 启用详细错误诊断:通过 *json.SyntaxError.Offset 快速定位JSON损坏位置;
- 避免盲目使用 ioutil.ReadAll:大XML可能耗尽内存,考虑流式处理或限制读取长度;
- XML字符串无需再解析:题干明确“不解析XML”,故获取后直接 []byte(ccdaBytes) 即可用于后续HTTP POST;
- 警惕BOM与编码:确保响应是UTF-8无BOM,否则 json.Unmarshal 可能静默失败。
综上,该问题本质是协议层契约不一致(期望JSON却收到XML)或数据序列化不规范。通过精准的响应诊断、严格的请求头控制,以及灵活的两阶段解析策略,即可在不改动服务端的前提下,稳健提取嵌套XML字符串。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











