You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Go语言中基于XML模板动态生成并修改XML文档?

在Go中动态解析并修改SOAP XML模板的实现方案

你已经用xml.Decoder.Token完成了解析环节,但要实现动态替换指定元素值并生成新XML,核心是跟踪当前元素的嵌套路径,在遇到目标元素的文本内容时替换值,最后用xml.Encoder将处理后的Token重新编码输出。下面是完整的实现思路和代码:

核心思路

  1. 跟踪元素路径:用切片维护当前嵌套的元素栈,判断文本内容属于哪个元素
  2. 替换逻辑匹配:定义参数映射存储客户端传入的键值对(键为目标元素名,值为替换内容)
  3. Token流处理:遍历每个Token,根据类型做不同处理:
    • StartElement:加入路径栈并直接写入输出
    • EndElement:移除路径栈最后一个元素并直接写入输出
    • CharData:判断当前元素是否在替换映射中,是则替换值,否则保留原内容
    • 其他Token(注释、指令等)直接透传输出

完整代码实现

package main

import (
	"bytes"
	"encoding/xml"
	"fmt"
	"strings"
	"testing"
)

// 客户端传入的替换参数,键对应wsGetInfosLink下的子元素名
type ReplaceParams map[string]string

func modifySOAPTemplate(template []byte, params ReplaceParams) ([]byte, error) {
	var output bytes.Buffer
	encoder := xml.NewEncoder(&output)
	encoder.Indent("", "   ") // 保持原模板的缩进风格,可根据需求移除

	decoder := xml.NewDecoder(bytes.NewReader(template))
	var elementPath []string // 跟踪当前元素的嵌套路径

	for {
		token, err := decoder.Token()
		if err != nil {
			if err.Error() == "EOF" {
				break
			}
			return nil, err
		}

		switch tt := token.(type) {
		case xml.StartElement:
			// 将当前元素加入路径栈
			elementPath = append(elementPath, tt.Name.Local)
			// 写入StartElement到输出
			if err := encoder.EncodeToken(tt); err != nil {
				return nil, err
			}
		case xml.EndElement:
			// 从路径栈移除当前元素
			if len(elementPath) > 0 {
				elementPath = elementPath[:len(elementPath)-1]
			}
			// 写入EndElement到输出
			if err := encoder.EncodeToken(tt); err != nil {
				return nil, err
			}
		case xml.CharData:
			trimmedContent := strings.TrimSpace(string(tt))
			// 保留原模板的空白缩进(换行、空格)
			if len(trimmedContent) == 0 {
				if err := encoder.EncodeToken(tt); err != nil {
					return nil, err
				}
				continue
			}

			// 判断当前元素是否需要替换值
			if len(elementPath) == 0 {
				if err := encoder.EncodeToken(tt); err != nil {
					return nil, err
				}
				continue
			}
			currentElement := elementPath[len(elementPath)-1]
			if replacement, ok := params[currentElement]; ok {
				// 替换为客户端传入的值
				newCharData := xml.CharData(replacement)
				if err := encoder.EncodeToken(newCharData); err != nil {
					return nil, err
				}
			} else {
				// 无替换需求,保留原内容
				if err := encoder.EncodeToken(tt); err != nil {
					return nil, err
				}
			}
		default:
			// 透传其他类型Token(如注释、XML指令等)
			if err := encoder.EncodeToken(token); err != nil {
				return nil, err
			}
		}
	}

	// 确保所有内容写入输出缓冲区
	if err := encoder.Flush(); err != nil {
		return nil, err
	}

	return output.Bytes(), nil
}

func TestModifySOAPTemplate(t *testing.T) {
	// 你的SOAP模板XML
	xmlTemplate := []byte(`<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://service.receive.appservice.jcms.hanweb.com">
   <soapenv:Header/>
   <soapenv:Body>
      <ser:wsGetInfosLink soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
         <nCataId xsi:type="xsd:int">3</nCataId>
         <bRef xsi:type="xsd:int">3</bRef>
         <nStart xsi:type="xsd:int">3</nStart>
         <nEnd xsi:type="xsd:int">3</nEnd>
         <bAsc xsi:type="xsd:int">3</bAsc>
         <strStartCTime xsi:type="xsd:string">gero et</strStartCTime>
         <strEndCTime xsi:type="xsd:string">sonoras imperio</strEndCTime>
         <strLoginId xsi:type="xsd:string">quae divum incedo</strLoginId>
         <strPwd xsi:type="xsd:string">verrantque per auras</strPwd>
         <strKey xsi:type="xsd:string">per auras</strKey>
      </ser:wsGetInfosLink>
   </soapenv:Body>
</soapenv:Envelope>`)

	// 客户端传入的替换参数
	params := ReplaceParams{
		"nCataId":       "10",
		"bRef":          "0",
		"nStart":        "0",
		"nEnd":          "20",
		"bAsc":          "1",
		"strStartCTime": "2024-01-01 00:00:00",
		"strEndCTime":   "2024-12-31 23:59:59",
		"strLoginId":    "test_user",
		"strPwd":        "test_pwd",
		"strKey":        "test_key",
	}

	modifiedXML, err := modifySOAPTemplate(xmlTemplate, params)
	if err != nil {
		t.Fatal(err)
	}

	fmt.Println(string(modifiedXML))
}

关键细节说明

  • 路径跟踪:通过elementPath切片准确识别当前文本所属的元素,支持多层嵌套的XML结构
  • 空白处理:保留原模板的缩进和换行,避免破坏SOAP格式的可读性
  • 命名空间兼容:如果需要匹配带命名空间的元素,只需将tt.Name.Space + ":" + tt.Name.Local作为路径键即可
  • Encoder格式化:encoder.Indent用于保持和原模板一致的缩进风格,不需要格式化时可直接删除该行

内容的提问来源于stack exchange,提问作者Fan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 10:21:31