使用包含的 xsd 文件编译架构

问题描述 投票:0回答:3

我有静态方法,我用它来根据 XSD 文件验证 XML 文件。这工作正常,直到有一个 XSD 文件包含另一个 XSD 文件。

例如,我遇到麻烦的地方:

类型.XSD:

<xs:simpleType name="MY_AMOUNT">
    <xs:restriction base="xs:decimal">
        <xs:maxInclusive value="999999999999.99"/>
        <xs:minInclusive value="-999999999999.99"/>
        <xs:totalDigits value="14"/>
        <xs:fractionDigits value="2"/>
    </xs:restriction>
</xs:simpleType>

主.XSD:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
    <xs:include schemaLocation="TYPES.xsd"/>
    <xs:element name="ROOT">
        <xs:complexType>
            <xs:sequence>
                <xs:element ref="SOMEREF1"/>
                <xs:element ref="SOMEREF2"/>
                <xs:element name="AMOUNT" type="MY_AMOUNT" minOccurs="0"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>

验证码:

public static class XmlUtils
{
    private static string Errors = string.Empty;

    public static bool ValidateAgainstXSD(string xmlFilePath, string xsdFilePath, ref string message)
    {
        try
        {
            var settings = new XmlReaderSettings();

            settings.ValidationType = ValidationType.Schema;
            settings.ValidationFlags = XmlSchemaValidationFlags.ProcessInlineSchema
                | XmlSchemaValidationFlags.ProcessInlineSchema
                | XmlSchemaValidationFlags.ReportValidationWarnings;
            settings.Schemas.Add(null, xsdFilePath);
            settings.Schemas.Compile();

            settings.ValidationEventHandler += (sender, args) =>
            {
                if (args.Severity == XmlSeverityType.Error)
                {
                    Errors += args.Message + "\n";
                }
            };

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

            message = Errors ?? string.Empty;
            return string.IsNullOrEmpty(Errors);
        }
        catch (Exception e)
        {
            message = "# error validating xml file: " + e.Message;
            return false;
        }
    }
}

不知何故,我似乎必须指定包含的 XSD 文件的路径,但我不知道在哪里。

错误发生在

settings.Schemas.Compile();
,它表示类型“MY_AMOUNT”未声明。我读到了有关自定义 XmlResolvers 的内容,但说实话,我没有让它发挥作用。

如果这对于答案很重要:xsd 文件始终位于同一目录中!

该方法的调用方式如下:

string msg = string.Empty;
string basedir = @"C:\Temp";
string xml = Path.Combine(basedir, "XML_FILE.xml");
string xsd = Path.Combine(basedir, "MAIN.xsd");

if (XmlUtils.ValidateAgainstXSD(xml, xsd, ref msg))
{
    // do some work
}
else
{
    Console.WriteLine(msg);
}

Console.ReadLine();

非常感谢任何帮助 - 谢谢!

2016-12-05更新:

我编写了自己的 XmlUrlResolver,看看幕后发生了什么:

internal class XUrlResolver : XmlUrlResolver
{
    public override object GetEntity(Uri absoluteUri, string role, Type ofObjectToReturn)
    {
        return base.GetEntity(absoluteUri, role, ofObjectToReturn);
    }

    public override Uri ResolveUri(Uri baseUri, string relativeUri)
    {
        return base.ResolveUri(baseUri, relativeUri);
    }
}

我只是尝试做:

XmlSchemaSet xset = new XmlSchemaSet();
xset.XmlResolver = new XUrlResolver();
xset.Add("", xsdFilePath);
xset.Compile();

现在发生了什么(在线

xset.Add
):

  1. XmlUrlResolver.ResolveUri(null,"C:\\Temp\\MAIN.XSD")
    -->
    {file:///C:/Temp/MAIN.xsd}
  2. XmlUrlResolver.ResolveUri(null,"C:\\Temp\\MAIN.XSD")
    -->
    {file:///C:/Temp/MAIN.xsd}
  3. XmlUrlResolver.GetEntity({file:///C:/Temp/MAIN.xsd})
    --> 文件流到 MAIN.xsd
  4. XmlUrlResolver.ResolveUri({file:///C:/Temp/MAIN.xsd},"TYPES.XSD")
    -->
    {file:///C:/Temp/TYPES.xsd}
  5. XmlUrlResolver.GetEntity({file:///C:/Temp/TYPES.xsd})
    --> 文件流到 TYPES.xsd

对我来说看起来不错(除了前 2 个调用是相等的!?!) - TYPES.XSD 的路径已按其应有的方式解析。

尽管如此,

xset.Compile()
会抛出异常:“未声明类型 MY_AMOUNT”

我不知道为什么:/

c# .net validation xsd xmlschemaset
3个回答
2
投票

首先您需要使您的 xsd 文件有效。

Types.xsd(添加架构根元素和 xs 命名空间)

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:simpleType name="MY_AMOUNT">
    <xs:restriction base="xs:decimal">
        <xs:maxInclusive value="999999999999.99"/>
        <xs:minInclusive value="-999999999999.99"/>
        <xs:totalDigits value="14"/>
        <xs:fractionDigits value="2"/>
    </xs:restriction>
</xs:simpleType>
</xs:schema>

Main.xsd(删除了无效引用)。

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
    <xs:include schemaLocation="TYPES.xsd"/>
    <xs:element name="ROOT">
        <xs:complexType>
            <xs:sequence>                
                <xs:element name="AMOUNT" type="MY_AMOUNT" minOccurs="0"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>

之后,鉴于两个 xsd 文件位于同一目录中,您的架构将可以正常编译。


1
投票

我遇到了同样的问题。

我并不是说这是正确的答案,但我通过将 Environment.CurrentDirectory 属性设置为包含的 XSD 所在的路径来解决这个问题。然后一切都处理得很好。

    


0
投票

所有解决方案和更正都不适合我
  • 相对包含路径经过验证是正确的。
  • 不同的运行时(VS T4 主机)正在成功解析架构
  • 我最终创建了一个自定义解析器实例,实现“GetEntity”方法,返回“ofObjectToReturn”类型,在我的例子中是“Stream”。我打开并返回了已解析包含的本地路径的流,它起作用了。

我注意到请求的 URI 的语法不同,因此这可能是麻烦的根源。

/// <summary> /// Custom XML-resolver for locating schema files /// </summary> private class SchemaXmlResolver : XmlResolver { /// <summary> /// <see cref="XmlResolver.GetEntity"/> /// </summary> /// <param name="absoluteUri"><see cref="XmlResolver.GetEntity"/></param> /// <param name="role"><see cref="XmlResolver.GetEntity"/></param> /// <param name="ofObjectToReturn"><see cref="XmlResolver.GetEntity"/></param> /// <returns><see cref="XmlResolver.GetEntity"/></returns> public override object GetEntity(Uri absoluteUri, string role, Type ofObjectToReturn) { // Open file return new FileStream(path: absoluteUri.LocalPath, mode: FileMode.Open, access: FileAccess.Read, share: FileShare.Read); } }

注意

:此实现对于用例来说肯定是高度特定的,因为它不尊重不同的 URI 类型和返回类型。

© www.soinside.com 2019 - 2024. All rights reserved.