将xsd:include xsd作为Visual Studio中的嵌入式资源进行处理
本文关键字:xsd 资源 嵌入式 处理 Studio include 作为 Visual | 更新日期: 2023-09-27 18:27:53
我有一组针对XMLSPY和Java代码进行验证的XSD。我需要将这组xsd作为嵌入资源带到Visual Studio 2012.net中。不幸的是,在尝试使用自定义XmlResolver来处理xsd:include时,我收到了一个错误,即已经声明了全局元素。错误很奇怪,因为元素只声明了一次。
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;
}
}
}
对这个问题的任何见解都将不胜感激。
结果是,从多个其他模式文档中多次包含同一个模式文档是用XSD创建互操作性噩梦的最简单方法。(不是唯一的方法,只是最简单的方法。)如果您拥有架构文档,请将导入时的所有包含和所有架构位置信息分离到驱动程序文件中,并从"正常"架构文档中删除导入时的全部包含和所有模式位置提示。
问题是,当您使用XMLSpy时,XSD是文件系统中的文件;然后发生的情况是,对于每个文件都有一个基本URI,因此解析器将使用该信息来确保一旦加载XSD,就不会根据URI比较再次加载同一个XSD。
现在,通过从程序集中以流的形式加载,所有信息都消失了(流没有基URI)。您的解析器将从不同的位置一次又一次地加载相同的XSD,从而产生这种冲突。
我所知道的所有XSD处理器都不使用任何其他方法来过滤同一XSD内容的多个包含,而是基本源URI。
在.NET中,最简单的方法可能是(同样,取决于图形的复杂程度)尝试本文中的解决方案;整个想法是提供一个基本URI,它应该提供避免多个包含所需的信息。
另一种选择可能是确保在自定义解析器中,对于您正在解析的任何给定URI,您只返回一次流(在所有其他情况下都返回null)。只要您不使用xsd:redefine组合(在这种情况下,解决方案是对模式文件图进行拓扑排序,并确保首先加载所有xsd:redfines),就可以保证这一点。
对于@CMSperbergMcQueen的观点,一种保证有效的方法是重构所有XSD,使每个命名空间只有一个XSD嵌入资源;每个XSD都将删除所有导入(称为"悬空"的技术)。将这些XSD作为独立的XSD添加到XML架构集并进行编译。除非遇到.NET错误,否则结果应该是已编译的XmlSchemaSet。