【问题标题】:Using XSDs with includes将 XSD 与包含一起使用
【发布时间】:2012-04-12 17:21:12
【问题描述】:

这是一个 XSD:

<?xml version="1.0"?>
<xsd:schema 
elementFormDefault='unqualified' 
attributeFormDefault='unqualified' 
xmlns:xsd='http://www.w3.org/2001/XMLSchema' 
>

  <xsd:simpleType name='TheSimpleType'>
    <xsd:restriction base='xsd:string' />
  </xsd:simpleType>
</xsd:schema>

这是第二个 XSD,包括上面的一个:

<?xml version="1.0" encoding="UTF-8" ?>
<xsd:schema 
elementFormDefault='unqualified' 
attributeFormDefault='unqualified' 
xmlns:xsd='http://www.w3.org/2001/XMLSchema' 
targetNamespace='a'
xmlns='a'
>

  <xsd:include schemaLocation='Include.xsd' />

  <xsd:element name = "TheElement" >
  <xsd:complexType>
  <xsd:attribute name="Code" type="TheSimpleType" use="required"/>
  </xsd:complexType>
  </xsd:element>
</xsd:schema>

我需要将(第二个)XSD 读入 C# 并且:

  1. 检查它是否是有效的 XSD,并且
  2. 根据它验证文档。

这里有一些 C# 可以在架构中阅读:

    XmlSchemaSet schemaSet = new XmlSchemaSet();
    foreach (string sd in Schemas)
    {
        using (XmlReader r = XmlReader.Create(new FileStream(sd, FileMode.Open)))
        {
            schemaSet.Add(XmlSchema.Read(r, null));
        }
    }
    schemaSet.CompilationSettings = new XmlSchemaCompilationSettings();
    schemaSet.Compile();

.Compile() 失败,因为“类型 'a:TheSimpleType' 未声明,或者不是简单类型。”

但是,如果满足以下任一条件,它就会起作用:

  • 命名空间已从架构中删除,或者
  • 命名空间被添加到包含中。

问题是:如何在不编辑模式的情况下让 C# 接受它?

我怀疑问题在于,虽然我已将两个模式都放入 XmlSchemaSet,但我仍然需要告诉 C# 一个包含在另一个中,即它自己没有解决。事实上,如果我只告诉 XmlSchemaSet 主 XSD(而不是包含)(都没有(或有)命名空间),那么“类型 'TheSimpleType' 没有声明,或者不是简单类型。”

因此这似乎是一个关于解决的问题包括:如何?!

【问题讨论】:

    标签: c# xml xsd xml-namespaces


    【解决方案1】:

    问题在于打开模式以供在线阅读的方式:

    XmlReader.Create(new FileStream(sd, FileMode.Open)
    

    我必须编写自己的XmlResolver,然后才能看到如何解析包含文件的路径:它来自可执行文件的目录,而不是来自父模式的目录。问题是父模式没有得到它的 BaseURI 集。以下是必须打开模式的方式:

    XmlReader.Create(new FileStream(pathname, FileMode.Open, FileAccess.Read),null, pathname)
    

    【讨论】:

    • 同样,感谢 Richard + 1。花了很长时间把我的头发拉到这个上面!
    • 当我使用 System.IO.Path(pathname) 作为 Create 的 baseUri 参数时,这对我有用。
    【解决方案2】:

    您可以使用XmlSchema.Includes 将它们链接在一起。然后,您只需将主架构添加到架构集中:

    var includeSchema = XmlSchema.Read(XmlReader.Create(...), null);
    var mainSchema = XmlSchema.Read(XmlReader.Create(...), null);
    
    var include = new XmlSchemaInclude();
    include.Schema = includeSchema;
    mainSchema.Includes.Add(include);
    
    var schemaSet = new XmlSchemaSet();
    schemaSet.Add(mainSchema);
    schemaSet.Compile();
    

    【讨论】:

    • +1 从来不知道XmlSchemaInclude 类。很好的答案。
    • 好的,很好。但是现在假设我必须在运行时确定所有包含,即,我给你一个带有包含的任意 XSD,你必须去获取它们。
    • s = XmlSchema.Read(r, null); 现在我看到我们有 s.IncludesXmlSchemaInclude 对象,并且它已正确填充(包含 1 个)。
    • 是的,这应该由 XmlSchema 类自动完成。我的猜测是它没有根据schemaLocation 属性找到正确的包含。您还可以查看XmlResolver 类。以here 为例。
    • 有没有办法将 schemaSet 对象(模式)写入 XML 文件?
    【解决方案3】:

    XmlSchemaSet 的默认行为是不尝试解析任何 XSD 包含的架构。为此,必须初始化 XmlResolver 属性。

    XmlSchemaSet schemas = new XmlSchemaSet
    {
        XmlResolver = new XmlUrlResolver()
    };
    

    此外,您必须按照@Richard Barraclough 的回答为 XmlReader 设置baseUri

    【讨论】:

      【解决方案4】:

      试试这个:D

      public static XmlSchema LoadSchema(string pathname)
      {
          XmlSchema s = null;
          XmlValidationHandler h = new XmlValidationHandler();
          using (XmlReader r = XmlReader.Create(new FileStream(pathname, FileMode.Open)))
          {
              s = XmlSchema.Read(r, new ValidationEventHandler(h.HandleValidationEvent));
          }
      
          if (h.Errors.Count > 0)
          {
              throw new Exception(string.Format("There were {1} errors reading the XSD at {0}. The first is: {2}.", pathname, h.Errors.Count, h.Errors[0]));
          }
      
          return s;
      }
      
      public static XmlSchema LoadSchemaAndResolveIncludes(string pathname)
      {
          FileInfo f = new FileInfo(pathname);
          XmlSchema s = LoadSchema(f.FullName);
      
          foreach(XmlSchemaInclude i in s.Includes)
          {
              XmlSchema si = LoadSchema(f.Directory.FullName + @"\" + i.SchemaLocation);
              si.TargetNamespace = s.TargetNamespace;
              i.Schema = si;
          }
      
          return s;
      }
      
      public static List<ValidationEventArgs> Validate(string pathnameDocument, string pathnameSchema)
      {
          XmlSchema s = LoadSchemaAndResolveIncludes(pathnameSchema);
      
          XmlValidationHandler h = new XmlValidationHandler();
      
          XmlDocument x = new XmlDocument();
          x.Load(pathnameDocument);
          x.Schemas.Add(s);
          s.Compile(new ValidationEventHandler(h.HandleValidationEvent));
          x.Validate(new ValidationEventHandler(h.HandleValidationEvent));
          return h.Errors;
      }
      

      特别注意si.TargetNamespace = s.TargetNamespace;

      显然,这假定包含被指定为相对于包含它们的架构的文件路径。

      【讨论】:

      • 这在(A 包括 B 和 C)和(B 引用但不包括 C)的情况下失败。确实有必要编写一个自定义的 XmlResolver 来解析文件名。
      【解决方案5】:

      这是我编写的用于处理 xsd 验证的方法。希望这对某人有所帮助。

              /// <summary>
              /// Ensure all xsd imported xsd documented are in same folder as master xsd
              /// </summary>
              public XsdXmlValidatorResult Validate(string xmlPath, string xsdPath, string xsdNameSpace)
              {
                  var result = new XsdXmlValidatorResult();
                  var readerSettings = new XmlReaderSettings {ValidationType = ValidationType.Schema};
                  readerSettings.ValidationFlags |= XmlSchemaValidationFlags.ProcessInlineSchema; 
                  readerSettings.ValidationFlags |= XmlSchemaValidationFlags.ProcessSchemaLocation;
                  readerSettings.Schemas.Add(null, xsdPath);
      
                  readerSettings.ValidationEventHandler += (sender, args) =>
                      {
                          switch (args.Severity)
                          {
                              case XmlSeverityType.Warning:
                                  result.Warnings.Add(args.Message);
                                  break;
                              case XmlSeverityType.Error:
                                  result.IsValid = false;
                                  result.Warnings.Add(args.Message);
                                  break;
                          }
                      };
      
                  var reader = XmlReader.Create(xmlPath, readerSettings);
      
                  while (reader.Read()) { }
      
                  return result;
              }
      

      【讨论】:

      • 这在我的情况下不起作用。所有 XSD(和 XML)文件都在同一个文件夹中。尽管如此,它告诉我包含的 xsd 中定义的类型没有定义
      猜你喜欢
      • 2013-04-13
      • 1970-01-01
      • 2016-02-22
      • 2013-06-23
      • 2014-04-19
      • 1970-01-01
      • 2012-12-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多