最稳组合是XmlSerializer反序列化为强类型对象再用JsonConvert序列化;遇无根/重复元素等不规则XML时改用XDocument转字典或匿名对象;避免直接用SerializeXNode()。
用 XmlSerializer + JsonConvert 是最稳的组合
直接上结论:c# 里 xml 转 json 不推荐手写解析,也不该依赖 dataset.readxml 这类间接路径。核心链路是先反序列化为强类型对象,再序列化为 json —— 这样语义清晰、可控性强、能自然处理命名冲突和空值。
常见错误是试图用 XDocument.ToString() 拼接 JSON 字符串,结果属性名大小写错、嵌套层级崩、null 变成字符串 "null",调试时连原始结构都对不上。
- 必须定义与 XML 结构严格匹配的 C# 类,字段名默认要跟 XML 元素名一致(或用
[XmlElement("xxx")]显式标注) -
XmlSerializer不支持直接读取带命名空间的 XML,遇到xmlns="http://..."必须传入XmlRootAttribute指定Namespace - 如果 XML 有混合内容(文本+子元素),别用
[XmlText]和[XmlElement]混用,容易抛InvalidOperationException
处理无根节点或不规则 XML 时用 XDocument + JObject.FromObject()
有些 XML 来自第三方接口,没有统一根节点,或者包含重复同名元素(如多个 <item>),这时强类型反序列化会失败。得退一步,用 XDocument 加载后转成字典或匿名对象再进 JSON 流程。
典型场景:解析 RSS、SOAP 响应体、或配置片段;这类 XML 往往没预定义 schema,靠运行时推导结构更实际。
- 用
XDocument.Load()或XDocument.Parse()加载后,调用Root?.Elements().ToDictionary(...)构建键值映射,注意重复 key 要转成List<JToken> - 别直接把
XElement丢给JsonConvert.SerializeXNode()—— 它会把属性转成@attr,文本内容变成#text,前端根本没法消费 - 若需保留原始顺序,别用
Dictionary,改用List<KeyValuePair<string, object>>,再用JObject.FromObject()转
JsonConvert.SerializeXNode() 的坑比想象中多
这个方法看着省事,但实际生产环境出问题频率很高。它不是“XML → JSON”的通用翻译器,而是按固定规则做节点投影,很多隐含行为会破坏数据语义。
典型错误现象:<price>19.99</price> 变成 {"price": {"#text": "19.99"}},而不是预期的 {"price": "19.99"};或者空元素 <desc /> 输出 {"desc": null},而前端期望的是 {"desc": ""}。
- 必须传
writeArrayAttribute: false,否则会把<items count="2">的属性也塞进 JSON,污染结构 -
omitRootObject: true很关键 —— 否则顶层会多包一层对象,比如{"root": {...}},而你只需要内部内容 - 它不处理 CDATA、注释、处理指令,遇到就静默跳过,日志里还不会报错
性能敏感场景下避免两次序列化
高频转换(如 API 网关层)里,走 XmlSerializer → object → JsonConvert 会有明显 GC 压力和 CPU 开销。这时候要绕过中间对象,用流式处理。
适用条件:XML 结构相对固定、不需要复杂校验、能接受 JSON 字段名硬编码。
- 用
XmlReader逐节点读取,同时用JsonTextWriter写入,手动控制字段名和嵌套层级 - 注意
XmlReader.Read()返回XmlNodeType.Element后,要立刻调用ReadStartElement(),否则后续ReadContentAsString()会跳过文本 - 空元素(
<tag/>)在XmlReader中是EmptyElement类型,需单独判断并输出""或null,不能等它自动推进到下一个节点
真正难的不是语法怎么写,而是搞清 XML 的实际结构有没有混合内容、属性是否承载语义、空值要不要透传。这些细节不提前确认,代码写完一半才发现 XmlSerializer 拒绝反序列化,或者 JSON 里突然冒出一堆 @ 和 # 开头的字段。










