1

XMLSPY および Java コードに対して検証する XSD のセットがあります。この XSD のセットを Visual Studio 2012 .net の埋め込みリソースとして取り込む必要があります。残念ながら、xsd:include を処理するためにカスタム XmlResolver でグローバル要素を解決しようとすると、グローバル要素が既に宣言されているというエラーが発生します。要素が一度しか宣言されていないため、エラーは奇妙です。

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

ユーティリティ クラスの検証

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;
        }
    }

}

この問題に関する洞察は大歓迎です。

4

2 に答える 2

0

問題は、XMLSpy を使用している場合、XSD がファイル システム内のファイルであることです。各ファイルにはベース URI があるため、リゾルバーはその情報を使用して、XSD が読み込まれると、URI 比較に基づいて同じものが再度読み込まれないようにします。

アセンブリからストリームとしてロードする方法では、すべての情報が失われます (ストリームにはベース URI がありません)。リゾルバーは、同じ XSD をさまざまな場所から何度もロードし続けるため、この衝突が発生します。

私が知っているすべての XSD プロセッサは、同じ XSD コンテンツの複数の包含をフィルタリングするために他の手段を使用していませんが、ソース URI をベースにしています。

.NET では、最も簡単な方法は (グラフの複雑さにもよりますが)この投稿のソリューションを試すことです。全体的なアイデアは、複数の包含を避けるために必要な情報を提供するベース URI を提供することです。

もう 1 つの方法は、カスタム リゾルバーで、解決する特定の URI に対して、ストリームを 1 回だけ返す (それ以外の場合はすべて null を返す) ことを確認することです。xsd:redefine コンポジションを使用していない限り、これは動作することが保証されています (その場合の解決策は、スキーマ ファイル グラフのトポロジカル ソートを作成し、すべての xsd:redefines が最初にロードされるようにすることです)。

@CMSperbergMcQueenのポイントでは、動作が保証されているアプローチは、名前空間ごとにXSDが埋め込まれたリソースが1つだけになるように、すべてのXSDをリファクタリングすることです。各 XSD では、すべてのインポートが削除されます (「ダングリング」と呼ばれる手法)。これらの XSD を独立した XSD として XML スキーマ セットに追加し、コンパイルします。.NET バグに遭遇しない限り、結果はコンパイル済みの XmlSchemaSet になります。

于 2013-08-16T16:53:29.387 に答える
0

XSD 1.0 では、バリデーターが同じスキーマ ドキュメントの複数のインクルード (またはインポート) を検出し、それらを 1 回だけインクルードすることを推奨していますが、必須ではありません。

その結果、他の複数のスキーマ ドキュメントから同じスキーマ ドキュメントを複数回含めることは、XSD との相互運用性の悪夢を作成する最も簡単な方法です。(唯一の方法ではなく、最も単純な方法です。) スキーマ ドキュメントを所有している場合は、インポートに関するすべてのインクルードとすべてのスキーマの場所の情報をドライバー ファイルに分離し、インポートに関するすべてのインクルードとすべてのスキーマの場所のヒントを「通常のドキュメント」から削除します。 ' スキーマ ドキュメント。

于 2013-08-16T14:04:14.077 に答える