帶有 XML 參數的 CLR 過程返回根級別的數據無效。第 1 行,位置 1
我有以下 CLR 儲存過程,該過程在此命令上引發錯誤
xmlDoc.LoadXml(inputXml);
程式碼
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; }
當 XML 文本作為文本在 Visual Studio 內的變數中時,它執行良好。但是當我將程式碼作為 CLR 上傳到 SQL 伺服器並嘗試從 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
然後拋出這個錯誤:
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)
我嘗試使用以下程式碼解決錯誤,因為我雖然 utf8 字節可能導致錯誤,但它沒有幫助。
// if byte mark exception happens string _byteOrderMarkUtf8 = Encoding.UTF8.GetString(Encoding.UTF8.GetPreamble()); if (inputXml.StartsWith(_byteOrderMarkUtf8)) { inputXml = inputXml.Remove(0, _byteOrderMarkUtf8.Length); }
這裡有什麼問題?
問題是您對該
SqlXml.ToString()
方法返回的值做出了錯誤的假設,然後在 Visual Studio 中進行測試時,您注入了一個值但沒有檢查ToString()
實際返回的內容(即:“System.Data.SqlTypes .SqlXml”)。SqlContext.Pipe.Send(inputXml);
儘管如果您將線放線上的上方,您可能會看到這一點xmlDoc.LoadXml(inputXml);
;-)。在使用這些
Sql*
類型時,您幾乎總是希望通過Value
它們都擁有的屬性來訪問它們。該Value
屬性確實返回了您想要獲取的 XML 的字元串版本。但是,這不太理想,因為您會將 XML 轉換為字元串,只是在您呼叫XmlDocument.LoadXml()
.更好的方法是使用
SqlXml.CreateReader()
返回 的方法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; } }
使用原始測試 SQL 執行上面的程式碼會在“消息”選項卡中返回以下內容:
Incorrect: System.Data.SqlTypes.SqlXml Better, but not an XmlDocument: <NodeA><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> Correct: <NodeA><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>
PS 你不需要擔心 UTF-8,特別是對於從 SQL Server 內部生成的 NCHAR / NVARCHAR / XML 數據,因為 SQL Server 是 UTF-16 LE。