【问题标题】:How to use CDATA in SQL XML如何在 SQL XML 中使用 CDATA
【发布时间】:2019-01-14 17:35:16
【问题描述】:

请有人帮我提供一个 XML 输出模板。客户要求我创建一个 xml 输出文件。然后,此文件将输入到客户的 CRM。这就是为什么,它必须与客户请求的模板完全匹配。除了 CDATA 之外,我已经设法在几个字段中完美地匹配它。

以下是可用于测试目的的查询。我需要 CDATA 包装字段客户和区域。我还附上了通过运行以下代码得到的输出

            If OBJECT_ID('tempdb..#Temp') is Not NULL
            Drop table #Temp

            CREATE TABLE #Temp
            (
                [ShiftDate] [date] NULL,
                [Ref_Num] [varchar](20) NULL,
                [Agency_Worker_Name] [varchar](100) NULL,
                [Client] [varchar](100) NULL,
                [Area] [VarChar] (100) Null,
                [Assignment] [varchar](20) NULL,
                [Contract_Start] [varchar](30) NULL,
                [Contract_End] [varchar](30) NULL,
                [Contract_BreakInMinutes] [varchar](10) NULL,
                [Contract_Total] [varchar](30) NULL,
                [Actual_Start] [varchar](30) NULL,
                [Actual_End] [varchar](30) NULL,
                [Actual_BreakInMinutes] [varchar](10) NULL,
                [Actual_Total] [varchar](30) NULL,
                [Commission] [decimal](18, 2) NULL,
                [Total_Cost] [decimal](18, 2) NULL,
                [Rate] [varchar](20) NULL,
                [OverallCost] [decimal](18, 2) NULL,
                [AgencybackingReport] [int] NULL,
                [AccountCode] [varchar](20) NULL
            )

            Insert Into #Temp
            Values 
            ('2018-07-24',
             '83076641',
             'ABCD',
             'ABCD',
             'ABCD',
             'CPA00',
             '09:00',
             '17:00',
             '30',
             '07:30',
             '10:30',
             '17:00',
             '30',
             '05:30',
             '28.49',
             '159.01',
             'Basic',
             '221.59',
             '1220883',
            ' ABCD')



            Declare @xml Int=(Select max(AgencyBackingReport)  From #Temp)

            select @xml As [@AgencyBackingReport],(Select 
            [Ref_Num] As [@reference],
            [ShiftDate] as [@startdate],
            Case When [AccountCode] is NULL Then 'Unknown' Else [AccountCode] End as [@accountcode],
            Contract_Start As 'PlannedShift/Start',
            Contract_End As 'PlannedShift/End',
            Contract_BreakInMinutes As 'PlannedShift/BreakinMinutes',
            Actual_Start As 'ActualShift/Start',
            Actual_End As 'ActualShift/End',
            Actual_BreakInMinutes As 'ActualShift/BreakinMinutes',
            OverallCost As [OverallCost],
            Agency_Worker_Name As 'AdditionalInformation/WorkerName',
            Client  As 'AdditionalInformation/Client',
            Area As 'AdditionalInformation/Area',
            -- ( select 
            --         1 as Tag ,
            --        0 as Parent ,
            --            (Select 
            --         Area 
            --        From #Temp M2
            --   Where M1.Ref_Num = m2.Ref_Num)
            --   As [Area!1!!CDATA]
            --   for xml explicit 
            --)   As 'AdditionalInformation/Area',
            Assignment As 'AdditionalInformation/Assignment',
            Commission As 'AdditionalInformation/Commission',
            Total_Cost As 'AdditionalInformation/TotalCost',
            Rate As 'AdditionalInformation/Rate'
            From #Temp M1
            for xml path('Shift'),Type)
            for XML Path('Shifts'),Type

【问题讨论】:

  • 这是一个好问题,从我这边 +1...

标签: sql-server xml tsql cdata


【解决方案1】:

您似乎知道CDATA 已经过时了...如果您想阅读有关此内容的内容,可以关注this link 和此答案中的链接。

有时我们必须坚持使用它...尤其是如果做得不好的第三方工具需要它。不过……

包含CDATA 部分的唯一方法是使用FOR XML EXPLICIT,但这相当笨拙。

您必须注意的一个问题:您不能将它存储在 XML 类型中!

每当您将包含 CDATA 部分的 XML 从字符串类型转换为原生 XML 时,您的 CDATA 都会丢失,并且将成为正确转义的普通 text() 节点。

试试这个

DECLARE @tbl TABLE(XmlAsString NVARCHAR(MAX), NativeXml XML);
INSERT INTO @tbl 
SELECT (
        SELECT 1      AS Tag
              ,NULL   AS Parent
              ,'test <&>' AS [SomeNode!1!!cdata]
        FOR XML EXPLICIT
        )
       ,(
        SELECT 1      AS Tag
              ,NULL   AS Parent
              ,'test <&>' AS [SomeNode!1!!cdata]
        FOR XML EXPLICIT
        );

SELECT * FROM @tbl

结果

<SomeNode><![CDATA[test <&>]]></SomeNode>   
<SomeNode>test &lt;&amp;&gt;</SomeNode>

简而言之:保留 CDATA 部分会强制您保持字符串类型。可能是一个很大的缺点......

回到你的问题

如果我没看错,你想得到上面的一切,但是

<Area><![CDATA[ABCD]]></Area>

...而不是

<Area>ABCD</Area>

方法一(丑):

按照上面的操作创建 XML,然后读取&lt;Area&gt; 的内容并在字符串级别使用REPLACE 来完全更改此节点。但是你绝不能将它转换回 XML...

方法2(笨拙):

这是你的带有CDATA 部分的XML,不是绝对完整的,但你看到了原则:

SELECT   1          AS Tag
        ,NULL       AS Parent
        ,1220883    AS [Shifts!1!AgencyBackingReport]
        ,NULL       AS [Shift!2!reference]
        ,NULL       AS [Shift!2!startdate]
        ,NULL       AS [Shift!2!accountcode]
        ,NULL       AS [PlannedShift!3!Start!Element]
        ,NULL       AS [PlannedShift!3!End!Element]
        ,NULL       AS [PlannedShift!3!BreakingMinutes!Element]
        ,NULL       AS [ActualShift!4!dummy!Element] --just a dummy
        ,NULL       AS [OverallCost!5]
        ,NULL       AS [AdditionalInformation!6!WorkerName!Element]
        ,NULL       AS [AdditionalInformation!6!Area!CDATA]
        ,NULL       AS [AdditionalInformation!6!Assignment!Element]
UNION ALL
SELECT 2
      ,1
      ,NULL
      ,83076641
      ,'2018-07-24'
      ,'ABCD'
      ,NULL
      ,NULL
      ,NULL
      ,NULL
      ,NULL
      ,NULL
      ,NULL
      ,NULL
UNION ALL
SELECT 3
      ,2
      ,NULL
      ,NULL
      ,NULL
      ,NULL
      ,'09:00'
      ,'17:00'
      ,30
      ,NULL
      ,NULL
      ,NULL
      ,NULL
      ,NULL
UNION ALL
SELECT 4 --just a dummy
      ,2
      ,NULL
      ,NULL
      ,NULL
      ,NULL
      ,NULL
      ,NULL
      ,NULL
      ,'dummy'
      ,NULL
      ,NULL
      ,NULL
      ,NULL
UNION ALL
SELECT 5 
      ,2
      ,NULL
      ,NULL
      ,NULL
      ,NULL
      ,NULL
      ,NULL
      ,NULL
      ,NULL
      ,211.59
      ,NULL
      ,NULL
      ,NULL
UNION ALL
SELECT 6
      ,2
      ,NULL
      ,NULL
      ,NULL
      ,NULL
      ,NULL
      ,NULL
      ,NULL
      ,NULL
      ,NULL
      ,'ABCD'
      ,'ABCD'
      ,'CPA00'
FOR XML EXPLICIT

结果

<Shifts AgencyBackingReport="1220883">
  <Shift reference="83076641" startdate="2018-07-24" accountcode="ABCD">
    <PlannedShift>
      <Start>09:00</Start>
      <End>17:00</End>
      <BreakingMinutes>30</BreakingMinutes>
    </PlannedShift>
    <ActualShift>
      <dummy>dummy</dummy>
    </ActualShift>
    <OverallCost>211.59</OverallCost>
    <AdditionalInformation>
      <WorkerName>ABCD</WorkerName>
      <Area><![CDATA[ABCD]]></Area>
      <Assignment>CPA00</Assignment>
    </AdditionalInformation>
  </Shift>
</Shifts>

【讨论】:

  • 非常感谢@shnugo 抽出时间从您忙碌的日常工作中抽出时间并如此详细地解释它。非常感谢。我刚刚尝试了您的解决方案,它对于一个时间表或一行数据非常有效,但是当我将它应用于我的表/多行时,它并没有给我我需要的东西。我将尝试附上所需输出的屏幕截图(仅当 Stackover flow 网站允许我这样做时),因为我昨天无法做到。
  • @user3482527 如果您在没有FOR XML EXPLICIT 的情况下调用SELECT,则会返回一个表。看看这个,了解它是如何工作的可能会有所帮助......
  • 非常感谢伙计,我现在就试试这个。我现在在上面的问题中附加了所需的 xml 输出。
  • @user3482527:由于您必须将结果保存在NVARCHAR(MAX) 中,因此最简单的方法是分别创建所有班次并使用简单的字符串连接来组合它们并添加根节点。更笨拙:-D
【解决方案2】:

在查询中使用 FOR XML。

E.g: select * from table1 FOR XML AUTO

这样的声明(基于臭名昭著的):

SELECT 
   CustomerID as "@CustomerID",
   CompanyName,
   Address as "address/street",
   City as "address/city",
   Region as "address/region",
   PostalCode as "address/zip",
   Country as "address/country",
   ContactName as "contact/name",
   ContactTitle as "contact/title",
   Phone as "contact/phone", 
   Fax as "contact/fax"
FROM Customers
FOR XML PATH('Customer')

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-12-18
    • 1970-01-01
    • 2016-09-24
    • 2019-02-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多