2013-08-16 102 views
1

我有一组XSD根据XMLSPY和Java代码进行验证。我需要将这组XSD作为Visual Studio 2012 .net中的嵌入式资源。不幸的是,我得到一个错误,即在尝试使用自定义XmlResolver解析它们以处理xsd:include时,已声明全局元素。错误很奇怪,因为元素只声明一次。处理xsd:将XSD作为Visual Studio中的嵌入式资源

Visual Studio Solution 
    |----------- Visual Studio Project 
      |----------- Schemas (Embedded Resource) 
        |----------- Directory A 
          |------------ set of XSDs that are referenced by XSDs in Directory B and to a globaltype definition file located in this directory 
        |----------- Directory B 
          |------------- set of XSDs that reference each other and those in Directory A, the XSD call from the main is located here 

验证的Util类

using System; 
using System.Collections.Generic; 
using System.IO; 
using System.Linq; 
using System.Reflection; 
using System.Xml; 
using System.Xml.Linq; 
using System.Xml.Schema; 

namespace ABC.XYZ.Utils 
{ 
    public static class XmlUtil 
    { 
     private static bool isValid; 

     public static bool ValidateXml(string targetNamespace, string schemaUri, string xml) 
     { 
      isValid = true; 

      var schemaReaderSettings = new XmlReaderSettings() { ValidationType = ValidationType.Schema }; 
      schemaReaderSettings.ValidationEventHandler += MyValidationHandler; 

      schemaReaderSettings.Schemas.XmlResolver = new XmlResourceResolver(); 
      var schemaReader = XmlReader.Create(GetSchemaStream(schemaUri), schemaReaderSettings); 
      schemaReaderSettings.Schemas.Add(targetNamespace, schemaReader); 

      var x = XElement.Parse(xml); 
      var sr = new System.IO.StringReader(x.ToString()); 

      XmlReader validatingReader = XmlReader.Create(sr, schemaReaderSettings); 

      while (validatingReader.Read()) 
      { 
      } 

      validatingReader.Close(); 

      return isValid; 
     } 

     private static void MyValidationHandler(object sender, ValidationEventArgs args) 
     { 
      Console.WriteLine("***Validation error"); 
      Console.WriteLine("\tSeverity:{0}", args.Severity); 
      Console.WriteLine("\tMessage:{0}", args.Message); 
      isValid = false; 
     } 

     private static Stream GetSchemaStream(string relativeFileName) 
     { 
      var resourceFileName = 
       Assembly.GetExecutingAssembly() 
        .GetManifestResourceNames() 
        .FirstOrDefault(p => p.EndsWith(relativeFileName)); 
      return Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceFileName); 
     } 

    } 
} 

定制的XmlResolver

using System; 
using System.Collections.Generic; 
using System.Diagnostics; 
using System.Globalization; 
using System.IO; 
using System.Linq; 
using System.Reflection; 
using System.Text; 
using System.Threading.Tasks; 
using System.Xml; 

namespace ABC.XYZ.Utils 
{ 
    public class XmlResourceResolver : XmlResolver 
    { 
     public const string AssemblyDefaultNamespace = "ABC.XYZ"; 
     public const string SchemasNamespace = "Schemas"; 

     public override Uri ResolveUri(Uri baseUri, string relativeUri) 
     { 
      var result = new UriBuilder("res://", AssemblyDefaultNamespace, -1, SchemasNamespace.Replace(".", "/")); 
      result.Path += "/" + relativeUri.Replace("../", "/").TrimStart('/'); 
      return result.Uri; 
     } 

     public override object GetEntity(Uri absoluteUri, string role, Type ofObjectToReturn) 
     { 
      if (absoluteUri.Scheme != "res") return null; 
      Debug.WriteLine("Loading resource based on location {0}", absoluteUri); 

      var assembly = Assembly.GetExecutingAssembly(); 
      var name = String.Format(CultureInfo.InvariantCulture, "{0}{1}", 
       absoluteUri.Host, 
       absoluteUri.GetComponents(UriComponents.PathAndQuery, UriFormat.Unescaped).Replace("/", ".")); 

      // try for an exact match based on schemaLocation hint path 
      var resourceName = (from x in assembly.GetManifestResourceNames() 
           where name.Equals(x, StringComparison.OrdinalIgnoreCase) 
           select x).FirstOrDefault(); 

      // if not match based on filename alone 
      if (resourceName == null) 
      { 
       var schemaDocumentName = Path.GetFileName(absoluteUri.AbsolutePath); 
       Debug.WriteLine("Unable to locate exact match, looking for match based on filename {0}", schemaDocumentName); 
       resourceName = (from x in assembly.GetManifestResourceNames() 
           where x.Contains(SchemasNamespace) && 
             x.EndsWith("." + schemaDocumentName, StringComparison.OrdinalIgnoreCase) 
           select x).FirstOrDefault(); 
      } 

      Debug.WriteLine("Loading resource {0}", resourceName); 
      var stream = assembly.GetManifestResourceStream(resourceName); 
      return stream; 
     } 
    } 

} 

任何见解此问题与将不胜感激。

回答

0

XSD 1.0鼓励但不要求验证器检测同一模式文档的多个包含(或导入)并且只包含它们一次。

结果是,从多个其他模式文档中多次包含相同的模式文档是使用XSD创建互操作性噩梦的最简单方法。 (不是唯一的方法,只是最简单的方法。)如果您拥有模式文档,请将导入的所有内容和所有模式位置信息分隔到驱动程序文件中,并删除从“normal”导入的所有包含和所有模式位置提示'架构文件。

0

问题是,当您使用XMLSpy进行操作时,XSD是文件系统中的文件;那么发生了什么事情,每个文件都有一个基本URI,因此解析器将使用这些信息来确保一旦加载了XSD,基于URI比较就不会再次加载同一个文件。

现在,您通过从程序集加载流的方式来执行此操作,所有信息都消失了(您的流没有基本URI)。您的解析器将不断从不同的地方反复加载相同的XSD,从而产生这种冲突。

我所知道的所有XSD处理器都没有采用任何其他方式来过滤同一XSD内容的多个内容,而是使用基本源URI。

在.NET中,最简单的方法可能是(再次,取决于图的复杂程度)尝试this post中的解决方案;整个想法是提供一个基本URI,它应该提供避免多重包含所需的信息。

另一种选择可能是确保在您的自定义解析器中,对于您要解析的任何给定URI,只返回一次流(在所有其他情况下返回null)。只要您不使用xsd:redefine组合(在这种情况下,解决方案是对模式文件图进行拓扑排序,并确保首先加载所有xsd:redefine),就可以保证工作。

对于@CMSperbergMcQueen来说,保证工作的方法是重构所有的XSD,这样每个名称空间只有一个XSD嵌入式资源;每个XSD都会删除所有的导入(技术称为“dangling”)。将这些XSD添加到设置为独立XSD的XML模式并编译。除非遇到.NET错误,否则结果应该是已编译的XmlSchemaSet。

相关问题