Golang XML格式化程序命名空间重复及XML声明移除需求
Golang XML格式化程序问题解决
问题说明
- 格式化后子标签重复父标签的命名空间声明
- 输出结果包含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
相关产品推荐
相关产品推荐

