在 SQL 服务器中以 XML 数据类型存储存储过程的文本

Storing the text of a stored procedure in an XML data type in SQL Server

我需要将数据库中所有存储过程的文本存储为 XML 数据类型。当我使用 FOR XML PATH 时,存储过程中的文本包含序列化数据字符,例如 
 用于 CRLF 和 " 等。我需要存储文本在 xml 结构中没有这些字符,因为文本将需要用于重新创建存储过程。

这是我用于 FOR XML PATH 的查询:

SELECT 
    [View].name AS "@VName", [Module].definition AS "@VDefinition"
FROM 
    sys.views AS [View] 
INNER JOIN 
    sys.sql_modules AS [Module] ON [Module].object_id = [View].object_id
FOR XML PATH ('View'), TYPE

我读到我应该使用 CDATA 作为使用 FOR XML EXPLICIT 的文本。但是,当我 运行 以下查询和查看 XML 数据时的输出,它也包含那些字符。我需要文本是没有这些字符的纯文本。

这是我的查询:

SELECT  
    1 AS Tag,
    0 AS Parent,
    NULL AS [Database1!1],      
    NULL AS [StoredProcedure!2!VName],
    NULL AS [StoredProcedure!2!cdata]

UNION ALL

SELECT  
    2 AS Tag,
    1 AS Parent,        
    NULL,
    [StoredProcedure].name as [StoredProcedure!2!!CDATA],
    [Module].definition as [StoredProcedure!2!!CDATA]
FROM 
    sys.procedures AS [StoredProcedure] 
INNER JOIN 
    sys.sql_modules [Module] ON [StoredProcedure].object_id = [Module].object_id
WHERE 
    [StoredProcedure].name NOT LIKE '%diagram%'
FOR XML EXPLICIT    

如何以纯文本形式存储存储过程的文本?或者当我解析 xml 数据类型以重新创建存储过程时,我可以反序列化它以便它没有那些字符吗?

理想情况下,我想使用 FOR XML PATH,但如果不可能,我会使用 FOR XML EXPLICIT

    SELECT 
        1 as Tag,  
        0 as Parent,    
        [View].name AS 'StoredProcedure!1!Name', 
        [Module].definition AS 'StoredProcedure!1!Definition!cdata'     
    FROM sys.views AS [View] 
    INNER JOIN sys.sql_modules AS [Module] ON [Module].object_id = [View].object_id
    FOR XML EXPLICIT 

Adventureworks2012 的输出示例:

    <StoredProcedure Name="vStoreWithContacts">
      <Definition><![CDATA[
    CREATE VIEW [Sales].[vStoreWithContacts] AS 
    SELECT 
        s.[BusinessEntityID] 
        ,s.[Name] 
        ,ct.[Name] AS [ContactType] 
        ,p.[Title] 
        ,p.[FirstName] 
        ,p.[MiddleName] 
        ,p.[LastName] 
        ,p.[Suffix] 
        ,pp.[PhoneNumber] 
        ,pnt.[Name] AS [PhoneNumberType]
        ,ea.[EmailAddress] 
        ,p.[EmailPromotion] 
    FROM [Sales].[Store] s
        INNER JOIN [Person].[BusinessEntityContact] bec 
        ON bec.[BusinessEntityID] = s.[BusinessEntityID]
        INNER JOIN [Person].[ContactType] ct
        ON ct.[ContactTypeID] = bec.[ContactTypeID]
        INNER JOIN [Person].[Person] p
        ON p.[BusinessEntityID] = bec.[PersonID]
        LEFT OUTER JOIN [Person].[EmailAddress] ea
        ON ea.[BusinessEntityID] = p.[BusinessEntityID]
        LEFT OUTER JOIN [Person].[PersonPhone] pp
        ON pp.[BusinessEntityID] = p.[BusinessEntityID]
        LEFT OUTER JOIN [Person].[PhoneNumberType] pnt
        ON pnt.[PhoneNumberTypeID] = pp.[PhoneNumberTypeID];
    ]]></Definition>
    </StoredProcedure>
    <StoredProcedure Name="vStoreWithAddresses">
      <Definition><![CDATA[
    CREATE VIEW [Sales].[vStoreWithAddresses] AS 
    SELECT 
        s.[BusinessEntityID] 
        ,s.[Name] 
        ,at.[Name] AS [AddressType]
        ,a.[AddressLine1] 
        ,a.[AddressLine2] 
        ,a.[City] 
        ,sp.[Name] AS [StateProvinceName] 
        ,a.[PostalCode] 
        ,cr.[Name] AS [CountryRegionName] 
    FROM [Sales].[Store] s
        INNER JOIN [Person].[BusinessEntityAddress] bea 
        ON bea.[BusinessEntityID] = s.[BusinessEntityID] 
        INNER JOIN [Person].[Address] a 
        ON a.[AddressID] = bea.[AddressID]
        INNER JOIN [Person].[StateProvince] sp 
        ON sp.[StateProvinceID] = a.[StateProvinceID]
        INNER JOIN [Person].[CountryRegion] cr 
        ON cr.[CountryRegionCode] = sp.[CountryRegionCode]
        INNER JOIN [Person].[AddressType] at 
        ON at.[AddressTypeID] = bea.[AddressTypeID];
    ]]></Definition>

如您所见,没有 &#xD; / &#xA; / &quot;/ etc 并且换行符表示为换行符

如果要在XML内存储特殊字符的数据,有两种选择(加一个笑话选项)

  • 转义
  • CDATA
  • 只是提一下:将所有内容转换为 base64 或类似的也可以:-)

重点是:你不需要这个!

CDATA(至少对我而言)的唯一原因是手动创建的内容(复制粘贴或输入)。每当您自动构建 XML 时,您都应该依赖隐式应用的转义。

为什么这会打扰您,在 XML 中的数据如何?

如果您正确阅读了这篇文章(不是使用 SUBSTRING 或其他基于字符串的方法),您将以 的原始外观.

找回它

试试这个:

DECLARE @TextWithSpecialCharacters NVARCHAR(100)=N'€ This is' + CHAR(13) + 'strange <ups, angular brackets! > And Ampersand &&&';

SELECT @TextWithSpecialCharacters FOR XML PATH('test');

returns

€ This is
strange &lt;ups, angular brackets! &gt; And Ampersand &amp;&amp;&amp;

但是这...

SELECT (SELECT @TextWithSpecialCharacters FOR XML PATH('test'),TYPE).value('/test[1]','nvarchar(100)');

...returns

€ This is
strange <ups, angular brackets! > And Ampersand &&&

Microsoft 甚至决定不支持 FOR XML(除了 EXPLICIT,这是一个令人头疼的问题...)

阅读关于 CDATA 的两个相关答案(由我 :-)

  • (带有更多链接...)

When I use, FOR XML PATH, the text within in the stored procedure contains serialized data characters like and for CRLF and ", etc.

是的,因为这就是 XML 的工作方式。举一个更清楚的例子,假设你的存储过程包含这个文本:

IF @someString = '<' THEN

然后将其存储在 XML 中,必须 应用某种编码,因为你不能在 < XML 中间(我希望你能明白为什么)。

真正的问题不是 'how do I stop my text being encoded when I store it as XML',而是(正如您猜测的那样):

Or when I parse the xml data type to recreate the stored procedure can I deserialize it so that it does not have those characters?

是的,这是您应该关注的方法。

您现在不知道我们如何从 XML 中获取您的文本。要记住的关键是你不能(或者更确切地说不应该)将 XML 视为 'text with extra bits' - 你应该使用理解 XML.

的方法

如果您要在 T-SQL 本身中提取文本,请使用各种 XQuery 选项。如果在 C# 中,请使用各种 XML 库中的任何一个。只是 不要 执行子字符串操作并期望它能工作...


举个例子,如果你在 T-SQL 中提取:

DECLARE @someRandomText nvarchar(max) = 'I am some arbitrary text, eg a sproc definition.

I contain newlines

And arbitrary characters such as < > & 

The end.';

-- Pack into XML

DECLARE @asXml xml = ( SELECT @someRandomText FOR XML PATH ('Example'), TYPE );

SELECT @asXml;


-- Extract

DECLARE @textOut nvarchar(max) = ( SELECT @asXml.value('.', 'nvarchar(max)') ) ;

SELECT @textOut;

但是您可以找到很多关于如何从 xml 类型的数据中获取值的教程;这只是一个例子。