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

Golang XML格式化程序命名空间重复及XML声明移除需求

Golang XML格式化程序问题解决

问题说明

  1. 格式化后子标签重复父标签的命名空间声明
  2. 输出结果包含XML声明行<?xml version="1.0"?>

解决方案

1. 移除XML声明行

xml.Decoder会将XML声明解析为xml.ProcInst类型的Token,只需在编码时跳过该类型Token即可。

2. 避免命名空间重复输出

xml.Decoder会将父元素的命名空间继承到子元素的StartElement属性中,导致编码时重复输出。我们可以通过记录已声明的命名空间,过滤掉子元素中重复的命名空间属性。

修改后的完整代码

package main

import (
    "encoding/xml"
    "errors"
    "io"
    "log"
    "os"
)

func main() {
    // 打开原始XML文件,添加错误处理
    openedFile, err := os.Open("originalFile.xml")
    if err != nil {
        log.Fatal(err)
    }
    defer openedFile.Close()

    xmlDecoder := xml.NewDecoder(openedFile)
    // 关闭严格模式,兼容更多XML格式
    xmlDecoder.Strict = false

    // 创建结果文件,添加错误处理
    createdFile, err := os.Create("result.xml")
    if err != nil {
        log.Fatal(err)
    }
    defer createdFile.Close()

    xmlEncoder := xml.NewEncoder(createdFile)
    xmlEncoder.Indent("", "  ")

    // 记录已声明的命名空间,key为空字符串表示默认命名空间
    declaredNamespaces := make(map[string]string)

    for {
        tokenXml, err := xmlDecoder.Token()
        if errors.Is(err, io.EOF) {
            break
        }
        if err != nil {
            log.Println(err)
            break
        }

        switch t := tokenXml.(type) {
        case xml.ProcInst:
            // 跳过XML声明
            continue
        case xml.StartElement:
            filteredAttrs := make([]xml.Attr, 0, len(t.Attr))
            for _, attr := range t.Attr {
                // 处理默认命名空间 xmlns="xxx"
                if attr.Name.Space == "" && attr.Name.Local == "xmlns" {
                    if _, exists := declaredNamespaces[""]; !exists {
                        filteredAttrs = append(filteredAttrs, attr)
                        declaredNamespaces[""] = attr.Value
                    }
                } else if attr.Name.Space == "xmlns" {
                    // 处理带前缀的命名空间 xmlns:prefix="xxx"
                    if _, exists := declaredNamespaces[attr.Name.Local]; !exists {
                        filteredAttrs = append(filteredAttrs, attr)
                        declaredNamespaces[attr.Name.Local] = attr.Value
                    }
                } else {
                    // 保留其他非命名空间属性
                    filteredAttrs = append(filteredAttrs, attr)
                }
            }
            // 替换过滤后的属性,再编码
            t.Attr = filteredAttrs
            xmlEncoder.EncodeToken(t)
        default:
            // 其他Token直接编码
            xmlEncoder.EncodeToken(t)
        }
    }

    if err := xmlEncoder.Close(); err != nil {
        log.Fatal(err)
    }
}

代码说明

  • 错误处理:补充了文件操作的错误检查,避免忽略潜在问题
  • 跳过XML声明:判断Token类型为xml.ProcInst时直接跳过,不进行编码
  • 命名空间过滤:用declaredNamespaces记录已声明的命名空间,处理每个开始标签时,只保留未声明过的命名空间属性,避免重复输出

测试结果

原始XML:

<TEST1 xmlns="text"><TEST2><TEST3></TEST3></TEST2></TEST1>

格式化后结果:

<TEST1 xmlns="text">
  <TEST2>
    <TEST3></TEST3>
  </TEST2>
</TEST1>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 19:07:17