用 XML 输出替换 sql 中的文本

Posted

技术标签:

【中文标题】用 XML 输出替换 sql 中的文本【英文标题】:replace text inside sql with XML output 【发布时间】:2016-02-22 16:28:59 【问题描述】:

我有这个问题:

SELECT [content_id]
      ,[content_html]
      ,[date_created]
      ,folder_id
  FROM content ct 

where folder_id=126
order by content_title
  FOR XML PATH('PressRelease'), ROOT ('PressReleases')

当我运行这个查询时,这是生成的 XML 文件:

<PressReleases>
  <PressRelease>
    <content_id>6442452927</content_id>
    <content_html>&lt;root&gt;&lt;Date&gt;2015-12-02&lt;/Date&gt;
        &lt;Description&gt;&lt;p class="customHeader"&gt;jobs to Philadelphia.&lt;/p&gt;
        &lt;p&gt;mtext in here.&lt;/p&gt;
        &lt;p&gt;mtext in here.&lt;/p&gt;
        &lt;/Description&gt;
        &lt;SEO&gt;&lt;h1&gt;Pennsylvania Location&lt;/h1&gt;
        &lt;div class="bulletRightBar"&gt; The move was made possible in part by the Philadelphia Jobs 
        Credit&lt;/div&gt;
&lt;/SEO&gt;
&lt;/root&gt;</content_html>
    <date_created>2015-12-02T09:47:12</date_created>
    <folder_id>126</folder_id>
  </PressRelease>
  <PressReleases>

我需要的是这个 XML 文件:

<PressReleases>
  <PressRelease>
    <content_id>6442452927</content_id>
    <content_html><root><Date>2015-12-02</Date>
        <Description>&lt;p class="customHeader"&gt;jobs to Philadelphia.&lt;/p&gt;
        &lt;p&gt;mtext in here.&lt;/p&gt;
        &lt;p&gt;mtext in here.&lt;/p&gt;
        </Description>
        <SEO>&lt;h1&gt;Pennsylvania Location&lt;/h1&gt;
        &lt;div class="bulletRightBar"&gt; The move was made possible in part by the Philadelphia Jobs 
        Credit&lt;/div&gt;
</SEO>
</root></content_html>
    <date_created>2015-12-02T09:47:12</date_created>
    <folder_id>126</folder_id>
  </PressRelease>
  <PressReleases>

&lt;content_html&gt; 中,我想将&lt;root&gt; &lt;date&gt;&lt;Description&gt; 设为XML 元素,但将其余部分保留为编码的html。

这是sql结果的截图

【问题讨论】:

请说明更多细节:“content_html”的数据类型,该列中的数据您将其放入XML 之前如何?能否请您设置一个SQL Fiddle 或过去一些真实的样本数据? @Shnugo 我为 sql 结果添加了一个 SC 看来你是在自找麻烦:) @JoePhilllips 为什么会这样? 您想将&lt;/Description&gt;&lt;SEO&gt; 保留为编码的HTML 还是将它们也进行转换 【参考方案1】:

这并不漂亮,但您可以将 XML 字段转换为字符串,使用 REPLACE 函数并将其转换回 XML,如下所示。您可能想要创建一个函数来执行此操作,因为该行会进行大量替换:

SELECT [content_id]
      ,cast(REPLACE(cast([content_html] as varchar(max)),'&lt;root&gt;','<root>') as xml)
      ,[date_created]
      ,folder_id
  FROM content ct 

where folder_id=126
order by content_title
  FOR XML PATH('PressRelease'), ROOT ('PressReleases')

或者这里是用函数调用它的方法

CREATE FUNCTION [dbo].[XML_Replace]
        (@XML_Field XML)
RETURNS XML 

BEGIN
 DECLARE @xml varchar(max) 
 DECLARE @xml_Mid varchar(max)
 DECLARE @strtBigInt bigint
 , @endBigInt bigint

 SET @xml = cast(@XML_Field as varchar(max))
 SET @strtBigInt = CHARINDEX('&lt;Description&gt;',@xml)
 SET @endBigInt = CHARINDEX('&lt;/SEO&gt;',@xml)

 SET @xml_Mid = SUBSTRING(@xml, @strtBigInt+19,@endBigInt-@strtBigInt-19);

 RETURN(cast(REPLACE(REPLACE(REPLACE(REPLACE(substring(@xml,0,@strtBigInt+19),'&lt;','<'),'&gt;','>') +    @xml_Mid +  REPLACE(REPLACE(substring(@xml,@endBigInt,Len(@xml)),'&lt;','<'),'&gt;','>'),'&lt;/Description&gt;','</Description>'),'&lt;SEO&gt;','<SEO>') as xml));
END

然后在你的代码中使用函数:

SELECT [content_id]
      ,dbo.XML_Replace([content_html]) as content_html
      ,[date_created]
      ,folder_id
  FROM content ct 

where folder_id=126
order by content_title
  FOR XML PATH('PressRelease'), ROOT ('PressReleases')

【讨论】:

第一种方法,您必须用正确的 XML 标记替换每个单独的标记。我刚刚添加的函数会查找开始和结束的描述标签,并分别更新它们之前和之后的所有 &lt;&gt; 如果您不希望您的 SEO 更新为 XML 标签,您只需要稍微编辑函数以供 @endBigInt 查找 SEO 结束标签,然后单独替换将&amp;lt;/Description&amp;gt; 替换为&lt;/Description&gt;@xml_Mid 字符串 该功能对您不起作用?你所期望的输出有什么问题? 它将为我解码的所有内容转换为节点,这不是我想要的......有一些我不想编码的 html 标签 'XML_Replace' is not a recognized built-in function name.【参考方案2】:

看起来[content_html] 存储为字符串(可能是nvarchar?)。它的内容看起来像一个格式良好的 XML。如果是这种情况,可以将CAST 转换为 XML 以正确合并到结果集中:

-- Test Data Set
WITH content AS (
    SELECT
        6442452927 AS [content_id],
        N'<root><Date>2015-12-02</Date>
        <Description><p class="customHeader">jobs to Philadelphia.</p>
        <p>mtext in here.</p>
        <p>mtext in here.</p>
        </Description>
        <SEO><h1>Pennsylvania Location</h1>
        <div class="bulletRightBar"> The move was made possible in part by the Philadelphia Jobs 
        Credit</div>
</SEO>
</root>' AS [content_html],
        CAST('2015-12-02T09:47:12' AS DATETIME2) AS [date_created],
        126 AS [folder_id],
        1 AS [content_title]
)
-- Query
SELECT [content_id]
        ,CAST((Select 
          CAST([content_html] AS XML).query('/root/Date/node()') AS [Date]
          ,CAST(CAST([content_html] AS XML).query('/root/Description/node()') AS nvarchar(max)) As [Description]
          ,CAST(CAST([content_html] AS XML).query('/root/SEO/node()') AS nvarchar(max)) AS [SEO]
          FOR XML PATH('root'), ROOT ('content_html')) As XML)
      ,[date_created]
      ,[folder_id]
  FROM content ct
WHERE folder_id=126
ORDER BY content_title
FOR XML PATH('PressRelease'), ROOT ('PressReleases')

【讨论】:

这会将我的查询中的所有内容解码为 HTML 标记 (),这不是我想要的 @Alundrathedreamwalker 这可以通过更多CASTs 来修复。【参考方案3】:

这方面的棘手之处在于您的content_html 字段既不包含XML 也不包含HTML。这是一种非常奇怪的数据编码方式。如果可能,您可能希望将字段中的数据转换为 XML,这将使查询变得非常容易。例如,您可以将您提供的数据中的字段转换为:

<root>
  <Date>2015-12-02</Date>
  <Description>&lt;p class="customHeader"&gt;jobs to Philadelphia.&lt;/p&gt;
        &lt;p&gt;mtext in here.&lt;/p&gt;
        &lt;p&gt;mtext in here.&lt;/p&gt;
        </Description>
  <SEO>&lt;h1&gt;Pennsylvania Location&lt;/h1&gt;
        &lt;div class="bulletRightBar"&gt; The move was made possible in part by the Philadelphia Jobs 
        Credit&lt;/div&gt;
</SEO>
</root>

如果您无法更改数据的存储方式,一种方法是使用下面的代码来回进行一些转换。子查询创建一个名为[content_xml] 的字段,它将content_html 字段中的数据转换为XML 数据类型。这假定您的所有数据都可以转换为有效的 XML。然后对于 XML 中 root 下的每个节点,它会重建节点并使用 CDATA 包装数据,以便保留所需的格式。

--set up test data
create table #content
    ([content_id] bigint
    ,[content_html] varchar(max)
    ,[date_created] datetime
    ,folder_id int
    , content_title varchar(50)
    )

insert into #content
values (
6442452927, 
'&lt;root&gt;&lt;Date&gt;2015-12-02&lt;/Date&gt;
        &lt;Description&gt;&lt;p class="customHeader"&gt;jobs to Philadelphia.&lt;/p&gt;
        &lt;p&gt;mtext in here.&lt;/p&gt;
        &lt;p&gt;mtext in here.&lt;/p&gt;
        &lt;/Description&gt;
        &lt;SEO&gt;&lt;h1&gt;Pennsylvania Location&lt;/h1&gt;
        &lt;div class="bulletRightBar"&gt; The move was made possible in part by the Philadelphia Jobs 
        Credit&lt;/div&gt;
&lt;/SEO&gt;
&lt;/root&gt;', 
'2015-12-02T09:47:12', 
126, 
'Content Title')

--Output
select [content_id],
    (
    SELECT
        CONVERT(xml,
        '<' + convert(varchar(max),T.c.query('local-name(.)')) + '>' 
        + (select convert(xml, '<![CDATA[' + convert(varchar(max),T.c.query('node()')) + ']]>') for xml path(''))
        + '</' + convert(varchar(max),T.c.query('local-name(.)')) + '>'
        )
    FROM content_xml.nodes('/root/*') T(c)
    for xml path(''), type
    ) as [content_html/root]
    ,[date_created]
    ,folder_id
from
    (
    SELECT *, 
        convert(xml,convert(xml,content_html).value('.','varchar(max)')) [content_xml]
    FROM #content ct 
    ) AllData
order by content_title
FOR XML PATH('PressRelease'), ROOT ('PressReleases')

【讨论】:

这对你有用吗?它可以满足您在测试用例中所说的需要。【参考方案4】:

请试试这个

SELECT [content_id]
      ,CONVERT(XML,REPLACE(REPLACE([content_html],'&lt;','<'),'&gt;','>')) AS [content_html]
      ,[date_created]
      ,folder_id
FROM content ct 
WHERE folder_id=126
ORDER BY content_title
FOR XML PATH('PressRelease'), ROOT ('PressReleases')

【讨论】:

以上是关于用 XML 输出替换 sql 中的文本的主要内容,如果未能解决你的问题,请参考以下文章

替换 PL/SQL 中的特殊 XML 字符

我用换行符替换所有标签`<br>`

如何从python中的文本文档中删除所有标点符号和其他符号?

用XSLT替换xml中的break元素

SQL 替换类型化 XML 数据

delphi中如何把有规律的文本文字放入STRINGGRID中的每个单元格中