我如何根据生成的 .Net class 或原始 XSD 中的 element/property 名称从 xml 文件中将值提取为字符串?

How can I extract values as strings from an xml file based on the element/property name in a generated .Net class or the original XSD?

我有一个大的复杂 XSD 集合。

我使用 xsd.exe 从那些 XSD 生成了 C# classes。当然,虽然生成的 classes 中的大多数属性是 strings,但许多是 decimals、DateTimes、enums 或 bools,就像他们应该的那样。

现在,我有一些以正确的 XML 格式构建的未验证数据,但很可能无法通过 XSD 验证,更不用说放入相关的实例中了.Net 对象。例如,在这个阶段,据我们所知,应该是 DateTime 的元素的值可能是 "ABC" - 甚至不能解析为 DateTime - 更不用说其他符合 maxLength 或正则表达式模式限制的字符串元素了。此数据已准备好传递到规则引擎,我们已经必须使所有内容有效,包括根据其他数据项适当设置默认值等。

我知道如何使用 System.Xml 中的各种类型按名称读取给定元素的字符串值。很明显,我可以手工编写代码以按名称找出今天存在的所有元素——但如果 XSD 发生变化,则需要重新编写代码。我希望能够直接读取 XSD 或在生成的 classes 上使用反射(必要时包括 [System.Xml.Serialization.XmlTypeAttribute(TypeName=...] 之类的属性)以准确找到如何递归查询 XML 将任何给定元素的原始、未经验证的字符串版本传递给规则集,然后在规则使其有效后,将其放回强类型对象或放回副本XML 用于序列化到对象中。

(我想到另一种方法是以某种方式自动生成对象的“stringly typed”版本 - 其中没有 DateTimes 等;只有字符串 - 并序列化 xml 到那个。我什至疯狂地想把 xsd.exe 生成的 .cs 文件和 search/replacing 所有不是字符串的枚举和基本类型转换为字符串,但必须有更好的方式。)

换句话说,是否有现有的通用方法从某些 XML 中提取 XElement 或属性值, 对应于 .Net 中的给定项目class如果它被序列化,没有实际序列化它?

很抱歉自己回答,也很抱歉我的回答中缺少实际代码,但我还没有得到我雇主的许可来分享这方面的实际代码。正在制作中,有动静会在这里更新

我能够实现我称之为 Tolerant XML Reader 的东西。与大多数 XML 反序列化不同,它首先使用反射来查看所需 .Net 类型的结构,然后 然后 尝试找到相关的 XElements 并解释它们。任何额外的元素都将被忽略(因为它们从未被查找过),任何未找到的元素都是默认的,任何找到的元素都会被进一步解释。

C#中的main方法签名如下:

public static T TolerantDeserializeIntoType<T>(
            XDocument doc,
            out List<string> messagesList,
            out bool isFromSuppliedData,
            XmlSchemaSet schemas = null,
            bool tolerant = true)

对它的典型调用可能如下所示:

List<string> messagesList;
bool defaultOnly;
SomeType result = TolerantDeserializeIntoType<SomeType>(someXDocument, out messagesList, out defaultOnly);

(您可以使用 var;在此示例中,为了清楚起见,我只是明确地将类型放在那里)。

这将采用 any XDocument(所以原始的唯一标准是它的格式正确),并创建指定类型的实例(SomeType,在这个例如)从它。

请注意,即使 XML 中的任何内容都没有被识别,它仍然不会失败。新实例将简单地使所有属性/public 字段为空或默认,MessageList 将列出所有已完成的默认设置,布尔输出参数将为 FALSE。

完成所有工作的递归方法具有相似的签名,只是它采用 XElement 而不是 XDocument,并且不采用 schemaSet。 (目前的实现还有一个明确的 bool 来指示默认为 false 的递归调用。这是一种稍微脏的方式,允许它在抛出错误之前收集所有失败消息,如果 tolerant 为 false;在未来的版本中我将重构它以仅公开 publicly 一个没有它的版本,如果我什至想要制作 XElement 版本 public):

public static T TolerantDeserializeXElementIntoType<T>(
            ref XElement element,
            ref List<string> messagesList,
            out bool isFromSuppliedValue,
            bool tolerant = true,
            bool recursiveCall = false)

工作原理,详情

从主调用开始,带有 XDocument 和可选 SchemaSet 的调用:

如果提供了一个将编译的模式集(实际上,它也寻找 xsi:noNamespaceSchemaLocation)初始 XDocument 和 schemaSet 调用在提供的 XDocument 中运行标准 XDocument.Validate(),但这只收集任何发出的验证错误回调。它不会抛出异常,并且只有两个原因:

  1. 它将为 MessageList 提供一些有用的消息,并且
  2. 它将填充所有 XElements 的 SchemaInfo 到 可能稍后在 XElement 版本中使用。

    (但是请注意, 模式完全是可选的。它实际上只是用来解决 C# 可能不清楚的一些模棱两可的情况 对象,如果给定的 XElement 是必需的或不是。)

从那里开始,在根节点和提供的 C# 类型上调用递归 XElement 版本

我已经让代码寻找由 xsd.exe 生成的 C# 对象的样式,尽管即使没有 xsd.exe 提供的 CustomAttributes,使用属性和字段的大多数基本结构化对象也可能工作,如果 Xml 元素的名称与对象的属性和字段相同。

该方法查找:

  • 数组
  • 简单值类型,明确:
    • 字符串
    • 枚举
    • 布尔值
    • 然后随便 否则通过使用相关的 TryParse() 方法,通过反射找到。
    • (注意 nulls/xsi:nill='true' 值也必须特别 处理)
  • 对象,递归。

它还在对象中为每个字段查找布尔值 'xxxSpecified' 或它找到的 属性 'xxx',并适当地设置它。这就是 xsd.exe 在 null 不够的情况下指示从 XML 中省略的元素的方式。

这是大纲。正如我所说,我可能会在适当的时候将实际代码放在 GitHub 之类的地方。希望这对某人有帮助。