【问题标题】:Validating xml nodes, not the entire document验证 xml 节点,而不是整个文档
【发布时间】:2009-04-03 20:32:31
【问题描述】:

我正在使用一些在 xml 中形成元素的 xml 'sn-ps'。我有架构,但我无法验证这些文件,因为它们不是完整的 xml 文档。这些 sn-ps 包含必要的父元素以在其他工具中使用时形成有效的 xml,因此我没有太多选择将它们变成有效的 xml 或更改架构。

是否可以验证一个元素,而不是整个文档? 如果没有,可以建议哪些解决方法?

我正在使用带有 .NET 2.0 框架的 C#。

【问题讨论】:

  • 如果您不能使用 XmlDocument 的根节点,我不确定如何使用 SelectSingleNode,您必须将 Xml 加载到该根节点中,如果它无效,则会失败.这让我觉得你不能使用XPath,除非你的文件本身是有效的。或者有什么办法吗?

标签: c# xml .net-2.0 xsd xml-validation


【解决方案1】:

我遇到了类似的问题,我只能验证部分 XML 文档。我在这里想出了这个方法:

private void ValidateSubnode(XmlNode node, XmlSchema schema)
{
    XmlTextReader reader = new XmlTextReader(node.OuterXml, XmlNodeType.Element, null);

    XmlReaderSettings settings = new XmlReaderSettings();
    settings.ConformanceLevel = ConformanceLevel.Fragment;
    settings.Schemas.Add(schema);
    settings.ValidationType = ValidationType.Schema;
    settings.ValidationEventHandler += new ValidationEventHandler(XSDValidationEventHandler);

    using (XmlReader validationReader = XmlReader.Create(reader, settings))
    {     
        while (validationReader.Read())
        {
        }
    }
}

private void XSDValidationEventHandler(object sender, ValidationEventArgs args)
{
    errors.AppendFormat("XSD - Severity {0} - {1}", 
                        args.Severity.ToString(), args.Message);
}

基本上,我将一个 XmlNode(我通过 .SelectSingleNode 从整个 XmlDocument 中选择)和一个​​ XML 模式传递给它,我从我的应用程序内的嵌入式资源 XSD 加载它。任何可能发生的验证错误都被填充到“错误”字符串构建器中,然后我在最后读出它,看看是否记录了任何错误。

为我工作 - 你的里程可能会有所不同:-)

【讨论】:

  • 这个解决方案效果很好。事实上,这看起来与我现在对文档进行的验证非常相似。我不知道它也可以应用于单个节点。回想起来,我想根本身就是一个节点。
  • 谢谢你,马克!这对我也有很大帮助。
  • Marc,您能否编辑此答案以不使用XmlTextReader,并使用using 块?我刚刚看到这段代码复制/粘贴。
  • @JohnSaunders:我更新了我的帖子——第二个XmlReaderusing 不是问题。对于XmlTextReader - 不太确定如何替换它。根据 XmlReader 文档,即使对于 .NET 4.5,从 XmlNode 读取似乎是“旧式”读者仍然可以接受/推荐的“边缘情况”之一。
【解决方案2】:

有一个XmlDocument.Validate 方法将XmlNode 作为参数,并且仅验证此节点。这可能就是您正在寻找的...

【讨论】:

  • 很遗憾,我目前无法访问我的项目,因此无法验证这一点。但是从阅读 MSDN 看来,这确实是我想要的。一直以来,我一直在寻找 XmlSchema...
  • 这不起作用。要验证的节点必须处于正确的“深度”。如果您想部分验证您的 xml 文件,此方法将非常有用。我没有有效的 xml 文件开头。
【解决方案3】:

好的,这是另一种方法:

您可以使用 XSLT 转换将架构文件转换为以您的 sn-p 元素为根的新架构。假设您的原始架构是

<xs:schema id="MySchema" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="RootElement">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="NestedElement">
          <xs:complexType>
            <xs:attribute name="Name" type="xs:string" use="required"/>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

您有想要验证的NestedElement 类型的 sn-ps:

<NestedElement Name1="Name1" />

然后你可以使用像这样的 XSLT 模板

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="xs:element[@name='NestedElement']"
                xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:schema id="MySchema">
      <xsl:copy-of select="."/>
    </xs:schema>
  </xsl:template>
</xsl:stylesheet>

创建一个以NestedElement 为根的新架构。生成的架构看起来像

<xs:schema id="MySchema" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="NestedElement">
    <xs:complexType>
      <xs:attribute name="Name" type="xs:string" use="required" />
    </xs:complexType>
  </xs:element>
</xs:schema>

然后您可以使用类似的代码针对这个新架构验证 sn-p 文档

XmlSchema schema;
using (MemoryStream stream =    new MemoryStream())
using (FileStream fs = new FileStream("MySchema.xsd", FileMode.Open))
using(XmlReader reader = XmlReader.Create(fs)) {
  XslCompiledTransform transform = new XslCompiledTransform();
  transform.Load("SchemaTransform.xslt");
  transform.Transform(reader, null, stream);
  stream.Seek(0, SeekOrigin.Begin);
  schema = XmlSchema.Read(stream, null);
}
XmlDocument doc = new XmlDocument();
doc.Schemas.Add(schema);
doc.Load("rootelement.xml");
doc.Validate(ValidationHandler);

MySchema.xsd 是原始模式,SchemaTransform.xslt 是转换(如上所示),rootelement.xml 是包含单个 sn-p 节点的 XML 文档。

【讨论】:

  • 这是一个非常优雅的解决方案。但是有一件事让我无法使用它。转换需要知道“NestedElement”的知识。这是一个问题,因为第三方可能会更新架构,并且需要更新/维护 xslt。
  • 您可以将元素名称(即“NestedElememt”)作为参数并从代码中 sn-p 的顶级元素中读取它。然后它也适用于多个 sn-p 元素...
  • 如果你有时间,也许你可以演示一下“喂食”参数技巧。我真的在这上面浪费了 3-4 个小时,却一无所获。实际上,提供参数很容易,诀窍是在 XSLT 1.0 上使用它:stackoverflow.com/questions/6117460/… 但我从来没有得到任何地方,即模板与我想要的不匹配。去字符串。现在替换所有东西,因为这很荒谬。 +1 解决方案。
【解决方案4】:

您可以使用特殊的命名空间别名来指定要验证的元素,然后只为该命名空间别名添加架构,而不为其他命名空间别名添加架构。这样,只有那些带有特殊命名空间前缀的元素才会被验证。

【讨论】:

  • 据我了解,这不适合,因为它需要编辑/创建模式。我无法更改架构,因为它是其他人每年使用和更新的协议。我只是在创建一个内部工具,因此必须尽量减少维护。
【解决方案5】:

似乎不可能做我渴望做的事。我目前的工作是创建一个空白模板 xml 文档。然后用我的 sn-p 替换所需的元素。从那里开始,我相信 Validate 方法将是可行的。但是动态创建这个模板本身似乎是另一项艰巨的任务。似乎没有任何简单的方法可以创建“骨架”文档。

【讨论】:

  • 我认为,XSLT 可能有用。查看我的新答案以获取一些建议
【解决方案6】:

我遇到了同样的问题。甚至在这里寻求解决方案。我找到了解决方法。

问题是只能验证根元素。所以...我在内存中编辑方案并将要验证的元素/类型添加到根

public static void AddElementToSchema(XmlSchema xmlSchema, string elementName, string elementType, string xmlNamespace)
{
    XmlSchemaElement testNode = new XmlSchemaElement();
    testNode.Name = elementName;
    testNode.Namespaces.Add("", xmlNamespace);
    testNode.SchemaTypeName = new XmlQualifiedName(elementType, xmlNamespace);
    xmlSchema.Items.Add(testNode);
    xmlSchema.Compile(XMLValidationEventHandler);
}

只需几行代码,您就不能修改或添加任何 XSD 文件 :) 通过对内存模式的这一简单更改,您可以使用用于验证完整文档的相同代码来验证片段。只需确保要验证的片段的根元素包含命名空间即可。 :)

【讨论】:

    猜你喜欢
    • 2012-10-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多