C# XmlReader.Create StringReader 从字符串创建读取器

2次阅读

xmlreader.create(String) 不支持直接传字符串,须用 stringreader 包装;需设 dtdprocessing.prohibit 和 xmlresolver=NULL 防 xxe;读取文本应避免直接用 innertext,推荐 readtofollowing + readelementcontentasstring。

C# XmlReader.Create StringReader 从字符串创建读取器

XmlReader.Create(string) 不能直接用,必须套一层 StringReader

直接传字符串给 XmlReader.Create("...") 会报错:NotSupportedException: this XmlReader implementation does not support reading from a string. 因为 Create 的重载里没有接受 string 类型的版本——它只认流(Stream)、文本阅读器(TextReader)或 URI。

正确做法是把 XML 字符串包进 StringReader,再传给 Create

var xml = "<root><item id="1"/></root>"; using var reader = XmlReader.Create(new StringReader(xml));
  • StringReaderTextReader子类,符合 Create 的参数要求
  • 别用 new StreamReader(new MemoryStream(Encoding.UTF8.GetBytes(xml))) —— 多余、慢、还可能因 bom 或编码不一致出问题
  • 如果 XML 来自用户输入或配置项,记得先做基础校验(比如非空、是否以 Create 阶段才崩

XmlReaderSettings 要设成 DtdProcessing.Prohibit 才安全

默认设置下,XmlReader 可能尝试解析外部 DTD,导致 XXE 攻击或网络请求失败(比如读到 .. SYSTEM "http://xxx"> 就卡住或抛异常)。

生产环境必须显式禁用:

var settings = new XmlReaderSettings {     DtdProcessing = DtdProcessing.Prohibit,     XmlResolver = null }; using var reader = XmlReader.Create(new StringReader(xml), settings);
  • 漏设 DtdProcessing.Prohibit 是常见疏忽,尤其本地测试时没暴露问题,上线后遇到恶意 XML 就挂
  • XmlResolver = null 是双重保险,防止某些老版本 .NET 忽略 DtdProcessing
  • 如果真要支持 DTD(极少见),改用 Parse 模式并自定义 XmlResolver,但绝大多数场景不需要

Read() 后别直接读 InnerText,容易跳过节点或重复读

新手常写 reader.Read(); console.WriteLine(reader.InnerText);,结果要么为空,要么内容错位。因为 Read() 移动到下一个节点,但不保证停在有文本内容的节点上(比如停在 StartElement 上,InnerText 就是空)。

  • 想取某个元素的文本值,先用 ReadToFollowing("elementName") 定位,再用 ReadElementContentAsString()
  • 想遍历所有文本节点,用 if (reader.NodeType == XmlNodeType.Text) { ... } 判断,而不是依赖 InnerText
  • InnerText 是“当前节点及其所有子节点的合并文本”,对嵌套结构容易误读;Value 更轻量,但只对 TextAttribute 等特定节点类型有效

StringReader 不支持 Seek,XmlReader 无法回退

StringReader 是单向流,一旦 XmlReader 读过去,就不能 reader.MoveToFirstAttribute()reader.ReadToPrevious() —— 这些方法会抛 InvalidOperationException

  • 需要多次遍历 XML?别反复用同一个 StringReader,每次新建一个:XmlReader.Create(new StringReader(xml))
  • 想随机访问节点,考虑用 XDocument.Load(new StringReader(xml)),虽然内存开销大,但灵活得多
  • 性能敏感场景(如高频解析小 XML),可缓存 XDocument 实例,但注意线程安全:XDocument 不是线程安全的,别共享实例

最麻烦的其实是调试:XmlReader 没有“当前 XML 片段”快照功能,出错时只能靠 reader.Namereader.Depthreader.LineNumber 拼上下文,建议解析前先打日志输出前 200 字符。

text=ZqhQzanResources