如何用Go解析指定结构XML并生成目标映射表
XML解析为Go映射表的解决方案
问题背景
给定test.xml文件,结构如下:
<metalib tagsetversion="1" name="TLog" version="1"> <struct name="STRUCT_1"> <entry name="ENTRY_1"/> </struct> <struct name="STRUCT_2"> <entry name="ENTRY_1"/> <entry name="ENTRY_2"/> </struct> <!-- many structs --> <union name="UNION" version="1"> <entry type="STRUCT_1" id="ID_STRUCT_1" /> <entry type="STRUCT_2" id="ID_STRUCT_2" /> </union> </metalib>
需要将其解析为Go中的两个映射表:
- 第一个映射表:键为结构体名称,值为该结构体下的所有entry名称列表,例如
map["STRUCT_1"] == ["ENTRY_1"] - 第二个映射表:键为union中entry的id,值为对应的结构体名称,例如
map2["ID_STRUCT_1"] == "STRUCT_1"
现有代码问题分析
你使用github.com/clbanning/mxj/x2j库的思路是对的,但代码中错误地使用了json.Unmarshal处理已经解析好的map——x2j.XmlToMap已经直接将XML转换成了嵌套的map[string]interface{},直接遍历这个结构即可提取数据,无需额外的JSON反序列化操作。
修正后的完整代码
package main import ( "fmt" "os" "github.com/clbanning/mxj/x2j" ) func main() { filePath := "test.xml" fh, err := os.Open(filePath) if err != nil { fmt.Println("打开文件失败:", err.Error()) return } defer fh.Close() // 使用XmlToMapReader直接读取并解析文件,简化代码 xmlMap, err := x2j.XmlToMapReader(fh) if err != nil { fmt.Println("解析XML失败:", err.Error()) return } // 提取metalib节点,断言为map类型 metalib, ok := xmlMap["metalib"].(map[string]interface{}) if !ok { fmt.Println("metalib节点格式不符合预期") return } // 初始化目标映射表 structEntryMap := make(map[string][]string) idStructMap := make(map[string]string) // 处理所有struct节点 structNodes, ok := metalib["struct"].([]interface{}) if ok { for _, node := range structNodes { structData, ok := node.(map[string]interface{}) if !ok { continue } // 获取结构体名称(mxj以"-属性名"存储XML属性) structName, ok := structData["-name"].(string) if !ok { continue } // 处理struct下的entry节点:可能是单个map或多个map组成的slice switch entryVal := structData["entry"].(type) { case map[string]interface{}: // 单个entry的情况 entryName := entryVal["-name"].(string) structEntryMap[structName] = []string{entryName} case []interface{}: // 多个entry的情况 var entryNames []string for _, entry := range entryVal { entryData := entry.(map[string]interface{}) entryNames = append(entryNames, entryData["-name"].(string)) } structEntryMap[structName] = entryNames } } } // 处理union节点下的entry unionNode, ok := metalib["union"].(map[string]interface{}) if ok { unionEntries, ok := unionNode["entry"].([]interface{}) if ok { for _, entry := range unionEntries { entryData := entry.(map[string]interface{}) structType := entryData["-type"].(string) structID := entryData["-id"].(string) idStructMap[structID] = structType } } } // 打印结果验证 fmt.Println("结构体->条目映射表:") for structName, entries := range structEntryMap { fmt.Printf("%s: %v\n", structName, entries) } fmt.Println("\nID->结构体映射表:") for id, structName := range idStructMap { fmt.Printf("%s: %s\n", id, structName) } }
关键说明
mxj库将XML元素的属性以-属性名的键存储在map中,例如<struct name="STRUCT_1">会被解析为map["-name"]: "STRUCT_1"- 当XML中存在多个同名节点时,mxj会将其解析为
[]interface{};若只有一个同名节点,则直接是map[string]interface{},代码中通过switch处理了这两种情况 - 使用
XmlToMapReader直接读取文件解析,简化了手动读取文件内容的代码
内容的提问来源于stack exchange,提问作者Kevin Tao
相关产品推荐
相关产品推荐

