带有 XML 参数的 CLR 过程返回根级别的数据无效。线路 1,位置 1

Muf*_*lix 3 sql-server-2008 xml sql-server c# sql-clr

我有以下 CLR 存储过程,该过程在此命令上引发错误

xmlDoc.LoadXml(inputXml);
Run Code Online (Sandbox Code Playgroud)

代码

    public static int spGetTaxOfficeXML(SqlXml _inputXml)
    {
        // this procedure rename Row elements name with NodeName attribute value
        string inputXml = _inputXml.ToString();
        XmlDocument xmlDoc = new XmlDocument();
        xmlDoc.LoadXml(inputXml);

// procedure Logic

        SqlContext.Pipe.Send(inputXml);
        return 0;
    }
Run Code Online (Sandbox Code Playgroud)

当 XML 文本作为文本位于 Visual Studio 内的变量内部时,效果很好。但是当我将代码作为 CLR 上传到 SQL Server 并尝试从 SQL Management Studio 执行它时:

DECLARE @XML XML
SET @XML = '<NodeA><NodeB></NodeB><NodeC AttributeX=""><Row NodeName="RowA" AttributeA="" AttributeB="abcd" AttributeC="efgh" /><Row NodeName="RowB" AttributeA="wxyz" /><Row NodeName="RowC" AttributeB="qwer" AttributeC="tyui" /><Row NodeName="RowD" AttributeA="stuv" AttributeB="erty" AttributeC="fghj" /></NodeC></NodeA>'

EXEC dbo.spGetTaxOfficeXML @XML
Run Code Online (Sandbox Code Playgroud)

然后抛出这个错误:

Msg 6522, Level 16, State 1, Procedure spGetTaxOfficeXML, Line 0
A .NET Framework error occurred during execution of user-defined routine or aggregate "spGetTaxOfficeXML": 
System.Xml.XmlException: Data at the root level is invalid. Line 1, position 1.
System.Xml.XmlException: 
   at System.Xml.XmlTextReaderImpl.Throw(Exception e)
   at System.Xml.XmlTextReaderImpl.ParseRootLevelWhitespace()
   at System.Xml.XmlTextReaderImpl.ParseDocumentContent()
   at System.Xml.XmlLoader.Load(XmlDocument doc, XmlReader reader, Boolean preserveWhitespace)
   at System.Xml.XmlDocument.Load(XmlReader reader)
   at System.Xml.XmlDocument.LoadXml(String xml)
   at StoredProcedures.spGetTaxOfficeXML(String inputXml)
Run Code Online (Sandbox Code Playgroud)

我尝试用以下代码解决该错误,因为我虽然 utf8 字节可能会导致错误,但它没有帮助。

// if byte mark exception happens
    string _byteOrderMarkUtf8 = Encoding.UTF8.GetString(Encoding.UTF8.GetPreamble());
    if (inputXml.StartsWith(_byteOrderMarkUtf8))
    {
        inputXml = inputXml.Remove(0, _byteOrderMarkUtf8.Length);
    }
Run Code Online (Sandbox Code Playgroud)

这里出了什么问题?

Sol*_*zky 5

问题在于,您对该SqlXml.ToString()方法返回的值做出了错误的假设,然后在 Visual Studio 中进行测试时,您注入了一个值,但没有检查ToString()实际返回的内容(即:“System.Data.SqlTypes” .SqlXml”)。SqlContext.Pipe.Send(inputXml);不过,如果您将该行放在该行上方,您可能会看到这一点xmlDoc.LoadXml(inputXml);;-)。

使用类型时Sql*,您几乎总是希望通过Value它们都具有的属性来访问它们。该Value属性确实返回您想要获取的 XML 的字符串版本。然而,这并不理想,因为您将把 XML 转换为字符串,然后在调用 时再次将其转换回 XML XmlDocument.LoadXml()

更好的方法是使用SqlXml.CreateReader()返回 an 的方法XmlReader,并且可以与该方法一起使用XmlDocument.Load()

我重新设计了您的测试代码(如下),作为显示所有这 3 个变体的示例:

using System.Data.SqlTypes;
using System.Xml;
using Microsoft.SqlServer.Server;

public partial class StoredProcedures
{
    [Microsoft.SqlServer.Server.SqlProcedure()]
    public static int spGetTaxOfficeXML(SqlXml InputXml)
    {
        // this procedure rename Row elements name with NodeName attribute value
        XmlDocument _XmlDoc = new XmlDocument();
        _XmlDoc.Load(InputXml.CreateReader());

        // procedure Logic

        SqlContext.Pipe.Send("Incorrect:\n" + InputXml.ToString());

        SqlContext.Pipe.Send("\nBetter, but not an XmlDocument:\n" + InputXml.Value);

        SqlContext.Pipe.Send("\nCorrect:\n" + _XmlDoc.OuterXml);

        return 0;
    }
}
Run Code Online (Sandbox Code Playgroud)

使用原始测试 SQL 运行上面的代码会在“消息”选项卡中返回以下内容:

using System.Data.SqlTypes;
using System.Xml;
using Microsoft.SqlServer.Server;

public partial class StoredProcedures
{
    [Microsoft.SqlServer.Server.SqlProcedure()]
    public static int spGetTaxOfficeXML(SqlXml InputXml)
    {
        // this procedure rename Row elements name with NodeName attribute value
        XmlDocument _XmlDoc = new XmlDocument();
        _XmlDoc.Load(InputXml.CreateReader());

        // procedure Logic

        SqlContext.Pipe.Send("Incorrect:\n" + InputXml.ToString());

        SqlContext.Pipe.Send("\nBetter, but not an XmlDocument:\n" + InputXml.Value);

        SqlContext.Pipe.Send("\nCorrect:\n" + _XmlDoc.OuterXml);

        return 0;
    }
}
Run Code Online (Sandbox Code Playgroud)

PS 您无需担心 UTF-8,特别是对于从 SQL Server 内部生成的 NCHAR / NVARCHAR / XML 数据,因为 SQL Server 是 UTF-16 LE。