jjzjj

c# - XslCompiledTransform和自定义XmlUrlResolver:“具有相同键的条目已存在”

coder 2024-06-25 原文

有没有办法调试由自定义xmlurlresolver从数据库加载的xslt文档,或者有人知道下面的错误消息是关于什么的?
我有一个导入公共xslt文档的xslt样式表:

<xsl:import href="db://common.hist.org"/>

方案由从数据库加载xslt文档的自定义XmlResolver处理,但我得到一个错误:
已存在具有相同密钥的条目。
xsl:import引用的公共xslt文档包含一些公共xslt模板,每个模板都有一个唯一的名称。
将xslt文档从本地文件系统移动到数据库后,便开始出现此错误。当使用指向本地文件的默认导入方案以及从本地文件系统加载xslt文档时,不会发生错误。
我还尝试在创建XslCompiledTransform实例时启用调试,但不知何故,无法“单步执行”基于数据库的xslt。
_xslHtmlOutput = new XslCompiledTransform(XSLT_DEBUG);

更新:以下基本上是请求的解析程序代码,但异常不会发生在我的代码中;因此我猜下面的代码中没有明显的原因。(这段代码实际上用于加载包含导入的xslt样式表,并且在注释出导入时,一切都按预期工作。)
public class XmlDBResolver : XmlUrlResolver
{
    private IDictionary<string,string> GetUriComponents(String uri)
    {
        bool useXmlPre = false;
        uri = uri.Replace("db://", "");
        useXmlPre = uri.StartsWith("xml/");
        uri = uri.Replace("xml/", "");
        IDictionary<string, string> dict = new Dictionary<string, string>();
        string app = null, area = null, subArea = null;

        if (!String.IsNullOrWhiteSpace(uri))
        {
            string[] components = uri.Split('.');

            if (components == null)
                throw new Exception("Invalid Xslt URI");

            switch (components.Count())
            {
                case 3:
                    app = components[0];
                    break;
                case 4:
                    area = components[0];
                    app = components[1];
                    break;
                case 5:
                    subArea = components[0];
                    area = components[1];
                    app = components[2];
                    break;
                default:
                    throw new Exception("Invalid Xslt URI");
            }

            dict.Add("application", app);
            dict.Add("area", area);
            dict.Add("subArea", subArea);
            dict.Add("xmlPreTransform", String.Format("{0}", useXmlPre));
        }

        return dict;
    }

    public override System.Net.ICredentials Credentials
    {
        set { /* TODO: check if we need credentials */ }
    }

    public override object GetEntity(Uri absoluteUri, string role, Type ofObjectToReturn)
    {
        /*
         *  db://<app>.hist.org
         *  db://<area>.<app>.hist.org
         *  db://<subArea>.<area>.<app>.hist.org
         * 
         * */

        Tracing.TraceHelper.WriteLine(String.Format("GetEntity {0}", absoluteUri));

        XmlReader reader = null;

        switch (absoluteUri.Scheme)
        {
            case "db":
                string origString = absoluteUri.OriginalString;
                IDictionary<string, string> xsltDict = GetUriComponents(origString);

                if(String.IsNullOrWhiteSpace(xsltDict["area"]))
                {
                    reader = DatabaseServiceFactory.DatabaseService.GetApplicationXslt(xsltDict["application"]);
                }
                else if (!String.IsNullOrWhiteSpace(xsltDict["area"]) && String.IsNullOrWhiteSpace(xsltDict["subArea"]) && !Boolean.Parse(xsltDict["xmlPreTransform"]))
                {
                    reader = DatabaseServiceFactory.DatabaseService.GetAreaXslt(xsltDict["application"], xsltDict["area"]);
                }
                else if (!String.IsNullOrWhiteSpace(xsltDict["area"]) && !String.IsNullOrWhiteSpace(xsltDict["subArea"]))
                {
                    if(Boolean.Parse(xsltDict["xmlPreTransform"]))
                        reader = DatabaseServiceFactory.DatabaseService.GetSubareaXmlPreTransformXslt(xsltDict["application"], xsltDict["area"], xsltDict["subArea"]);
                    else
                        reader = DatabaseServiceFactory.DatabaseService.GetSubareaXslt(xsltDict["application"], xsltDict["area"], xsltDict["subArea"]);
                }
                return reader;

            default:
                return base.GetEntity(absoluteUri, role, ofObjectToReturn);
        }
    }

为完整起见,IDatabaseService接口(相关部分):
public interface IDatabaseService
{
    ...
    XmlReader GetApplicationXslt(String applicationName);
    XmlReader GetAreaXslt(String applicationName, String areaName);
    XmlReader GetSubareaXslt(String applicationName, String areaName, String subAreaName);
    XmlReader GetSubareaXmlPreTransformXslt(String applicationName, String areaName, String subAreaName);
}

更新:我试图通过临时从web服务器加载样式表来隔离问题,这是可行的。我了解到,与存储在web服务器上的样式表相比,sql server显然只存储没有xml声明的xml片段。
更新:异常的堆栈跟踪:
system.xml.xsl.xslloadexception:xslt komplierungsfehler。费勒·贝(91616)。--->System.ArgumentException:已存在具有相同密钥的条目..bei system.collections.specialized.listdioctionary.add(object key,object value)bei system.collections.specialized.hybriddioctionary.add(object key,object value)bei system.xml.xsl.xslt.xsltloader.loadstylesheet(xml reader reader,boolean include)bei system.xml.xsl.xslt.xsltloader.loadstylesheet(uri uri,boolean include)bei system.xml.xsl.xslt.xsltloader.loadstylesheet(xml reader reader,boolean include)--ende der inneral ablauverforforgung des ausnamestacks---bei system.xml.xsl.xslt.xsltloader.loadstylesheet(xmlreader读取器,布尔值包含)bei system.xml.xsl.xslt.xsltloader.load(xml reader reader)bei system.xml.xsl.xslt.xsltloader.load(编译器编译器,对象样式表,xmlresolver xmlresolver)bei system.xml.xsl.xslt.compiler.compile(对象样式表,xmlresolver xmlresolver,qilexpression&qil)bei system.xml.xsl.xslcompiledtransform.loadinternal(对象样式表、xsltsettings设置、xmlresolver样式表解析器)bei system.xml.xsl.xslcompiledtransform.load(字符串样式表uri、xsltsettings设置,xmlresolver样式表解析器)bei(我的命名空间和类)。getxsltransform(布尔预转换)bei(我的命名空间和类)。get_xslhtmloutput()bei(我的命名空间和类)。get_displaymarkup()

最佳答案

简短回答:
您的IDatabaseService接口方法返回XmlReader对象。在构造这些函数时,请确保将abaseUri传递给构造函数;例如:

public XmlReader GetApplicationXslt(string applicationName)
{
    …
    var baseUri = string.Format("db://{0}.hist.org", applicationName);
    return XmlReader.Create(input: …, 
                            settings: …,
                            baseUri: baseUri);  // <-- this one is important!
}

如果指定此参数,一切都可能正常工作。请看这个答案的最后一部分,看看我为什么要提出这个建议。
长答案,简介:可能的错误源:
首先,让我们简单考虑一下哪些组件可能导致错误:
“将xslt文档从本地文件系统移动到数据库后,就开始出现此错误。当使用指向本地文件的默认导入方案以及从本地文件系统加载xslt文档时,不会发生错误。”
将样式表放入数据库意味着您必须…
更改了样式表中的导入路径(引入了db://…路径)
实现并连接一个自定义的XmlDbResolver来处理db://导入方案
IDatabaseService的形式实现数据库访问代码,它支持XmlDbResolver
如果样式表除了导入路径之外没有变化,那么错误可能出现在XmlResolver类和/或IDatabaseService实现中。由于您尚未显示后者的代码,因此我们无法在不进行猜测的情况下调试您的代码。
我已经使用您的XmlDbResolver创建了一个模拟项目(下面是完整的描述)。我无法重现错误,因此我怀疑是您的IDatabaseService实现导致了错误。
更新:我已经能够重现这个错误。请参阅操作人员的评论和本答案的最后一部分。
我试图重现你的错误:
我在visual studio 2010中创建了一个控制台应用程序项目(您可以通过使用git(git clone https://gist.github.com/fbbd5e7319bd6c281c50b4ebb1cee1f9.git)克隆this Gist,然后签出第二次提交git checkout d00629,来检索该项目)。我将在下面更详细地描述每个解决方案项。
(请注意,SqlServerDatabase.mdfTestInput.xml.xslt项目项的copy to output directory属性都应设置为always。)
sqlserver数据库.mdf:
这是一个基于服务的数据库,我将把它附加到sqlserverexpress2008的本地实例。(这是通过App.config中的连接字符串完成的;请参见下文。)
我已在此数据库中设置了以下项:
此表包含两列,定义如下:
表最初是空的。测试数据将在运行时添加到数据库中(请参见下面的Program.csCommonHistOrg.xslt)。
App.config:
此文件包含上述数据库的连接字符串项。
<?xml version="1.0"?>
<configuration>
  <connectionStrings>
    <add name="SqlServerDatabase"
         connectionString="Data Source=.\SQLEXPRESS;
                           AttachDbFilename=|DataDirectory|\SqlServerDatabase.mdf;
                           Integrated Security=True;
                           User Instance=True"
         />
  </connectionStrings>
</configuration>

idatabaseservice.cs:
这个文件包含IDatabaseService接口的定义,我在这里不再重复。
sqlserverdatabaseservice.cs:
它包含一个实现IDatabaseService的类。它将数据读/写到上述数据库:
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using System.IO;
using System.Xml;

class SqlServerDatabaseService : IDatabaseService
{
    // creates a connection based on connection string from App.config: 
    SqlConnection CreateConnection()
    {
        return new SqlConnection(connectionString: ConfigurationManager.ConnectionStrings["SqlServerDatabase"].ConnectionString);
    }

    // stores an XML document into the 'ApplicationDocuments' table: 
    public void StoreApplicationDocument(string applicationName, XmlReader document)
    {
        using (var connection = CreateConnection())
        {
            SqlCommand command = connection.CreateCommand();
            command.CommandText = "INSERT INTO ApplicationDocuments (ApplicationName, Document) VALUES (@applicationName, @document)";
            command.Parameters.Add(new SqlParameter("@applicationName", applicationName));
            command.Parameters.Add(new SqlParameter("@document", new SqlXml(document)));
                                                             //  ^^^^^^^^^^^^^^^^^^^^
            connection.Open();
            int numberOfRowsInserted = command.ExecuteNonQuery();
            connection.Close();
        }
    }

    // reads an XML document from the 'ApplicationDocuments' table:
    public XmlReader GetApplicationXslt(string applicationName)
    {
        using (var connection = CreateConnection())
        {
            SqlCommand command = connection.CreateCommand();
            command.CommandText = "SELECT Document FROM ApplicationDocuments WHERE ApplicationName = @applicationName";
            command.Parameters.Add(new SqlParameter("@applicationName", applicationName));

            connection.Open();
            var plainXml = (string)command.ExecuteScalar();
            connection.Close();

            if (plainXml != null)
            {
                return XmlReader.Create(new StringReader(plainXml));
            }
            else
            {
                throw new KeyNotFoundException(message: string.Format("Database does not contain a application document named '{0}'.", applicationName));
            }
        }
    }

    … // (all other methods throw a NotImplementedException)
}

xmldbsolver.cs:
它包含XmlDbResolver类,与您的XmlDBResolver类相同,但有两个更改:
公共构造函数接受一个IDatabaseService对象。这是用来代替DatabaseServiceFactory.DatabaseService
我不得不取消对Tracing.TraceHelper.WriteLine的呼叫。
commonhistorg.xslt:
这是db://common.hist.org样式表,将在运行时放入数据库(请参见下面的Program.cs):
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="Foo">
    <Bar/>
  </xsl:template>
</xsl:stylesheet>

测试样式表.xml:
这是引用上述样式表的样式表:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:import href="db://common.hist.org"/>
</xsl:stylesheet>

testinput.xml:
这是我们将使用上面的db://common.hist.org转换的xml测试输入文档:
<?xml version="1.0" encoding="utf-8" ?>
<Foo/>

Program.cs:
它包含测试应用程序代码:
using System;
using System.Text;
using System.Xml;
using System.Xml.Xsl;

class Program
{
    static void Main(string[] args)
    {
        var databaseService = new SqlServerDatabaseService();

        // put CommonHistOrg.xslt into the 'ApplicationDocuments' database table:
        databaseService.StoreApplicationDocument(
            applicationName: "common",
            document:        XmlReader.Create("CommonHistOrg.xslt"));

        // load the XSLT stylesheet:
        var xslt = new XslCompiledTransform();
        xslt.Load(@"TestStylesheet.xslt", 
            settings: XsltSettings.Default, 
            stylesheetResolver: new XmlDbResolver(databaseService));

        // load the XML test input:
        var input = XmlReader.Create("TestInput.xml");

        // transform the test input and store the result in 'output':
        var output = new StringBuilder();
        xslt.Transform(input, XmlWriter.Create(output));

        // display the transformed output:
        Console.WriteLine(output.ToString());
        Console.ReadLine();
    }
}

在我的机器上工作起来就像一个符咒:输出是一个带有空根元素TestStylesheet.xslt的xml文档,这就是<Bar/>样式表从测试输入中为匹配的db://common.hist.org元素输出的内容。
更新:错误复制和修复:
<Foo/>方法中插入以下语句:
databaseService.StoreApplicationDocument(
    applicationName: "test",
    document:        XmlReader.Create("TestStylesheet.xslt"));

而不是
xslt.Load(@"TestStylesheet.xslt", …);


xslt.Load(@"db://test.hist.org", …);

这将触发操作报告的错误。
经过一番调试,我发现以下情况并没有导致这个问题。
数据库表中的Main列具有Document类型。它也会因XML而失败。
从数据库返回的文档中缺少NTEXT头的事实。即使在<?xml … ?>将控制权返回到框架之前手动添加回xml头,错误仍然存在。
实际上,错误是在.NET框架代码中的某个地方触发的。所以我决定下载并安装.NET Framework reference source。(我将解决方案更改为使用框架的3.5版进行调试。)安装此解决方案并重新启动VS之后,您就可以在调试会话期间查看和逐步查看框架代码。
从调用SqlServerDatabaseService方法开始,我进入了框架代码,最终在xslt.Load(…;)中找到了一个方法。有一个名为MainLoadStylesheet,它显然存储了已加载样式表的基本uri。因此,如果我们加载多个带有空的或丢失的基uri的样式表,此方法将尝试将XsltLoader.cs添加到该字典两次;这就是导致错误的原因。
因此,一旦为HybridDictionary返回的每个样式表分配了一个唯一的基uri,一切都应该正常工作。您可以通过将documentUrisInUse传递给null构造函数来完成此操作。请参阅我的答案开头的代码示例。您还可以通过downloading或克隆this GistIDatabaseService)检索更新的工作解决方案。

关于c# - XslCompiledTransform和自定义XmlUrlResolver:“具有相同键的条目已存在”,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11864564/

有关c# - XslCompiledTransform和自定义XmlUrlResolver:“具有相同键的条目已存在”的更多相关文章

  1. ruby - Facter::Util::Uptime:Module 的未定义方法 get_uptime (NoMethodError) - 2

    我正在尝试设置一个puppet节点,但ruby​​gems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由ruby​​gems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby

  2. ruby - 具有身份验证的私有(private) Ruby Gem 服务器 - 2

    我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..

  3. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

    我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

  4. ruby-on-rails - form_for 中不在模型中的自定义字段 - 2

    我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢

  5. ruby - 主要 :Object when running build from sublime 的未定义方法 `require_relative' - 2

    我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby​​1.9+ 关于ruby-主要:Objectwhenrun

  6. ruby - 在 Ruby 中有条件地定义函数 - 2

    我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin

  7. ruby - 定义方法参数的条件 - 2

    我有一个只接受一个参数的方法:defmy_method(number)end如果使用number调用方法,我该如何引发错误??通常,我如何定义方法参数的条件?比如我想在调用的时候报错:my_method(1) 最佳答案 您可以添加guard在函数的开头,如果参数无效则引发异常。例如:defmy_method(number)failArgumentError,"Inputshouldbegreaterthanorequalto2"ifnumbereputse.messageend#=>Inputshouldbegreaterthano

  8. ruby - 如何在 Grape 中定义哈希数组? - 2

    我使用Ember作为我的前端和GrapeAPI来为我的API提供服务。前端发送类似:{"service"=>{"name"=>"Name","duration"=>"30","user"=>nil,"organization"=>"org","category"=>nil,"description"=>"description","disabled"=>true,"color"=>nil,"availabilities"=>[{"day"=>"Saturday","enabled"=>false,"timeSlots"=>[{"startAt"=>"09:00AM","endAt"=>

  9. ruby - 获取模块中定义的所有常量的值 - 2

    我想获取模块中定义的所有常量的值:moduleLettersA='apple'.freezeB='boy'.freezeendconstants给了我常量的名字:Letters.constants(false)#=>[:A,:B]如何获取它们的值的数组,即["apple","boy"]? 最佳答案 为了做到这一点,请使用mapLetters.constants(false).map&Letters.method(:const_get)这将返回["a","b"]第二种方式:Letters.constants(false).map{|c

  10. c# - 如何在 ruby​​ 中调用 C# dll? - 2

    如何在ruby​​中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL

随机推荐