【问题标题】:Extract data from xml stored as nvarchar从存储为 nvarchar 的 xml 中提取数据
【发布时间】:2016-10-18 03:20:42
【问题描述】:

我必须从存储为nvarcharXML 中提取电子邮件Subject

我正在使用这个查询:

SELECT rtrim((SELECT CAST(
                    SUBSTRING(
                        [XML]
                        ,patindex('%<SUBJECT>%', [XML])
                        ,patindex('%</SUBJECT>%', [XML])-patindex('%<SUBJECT>%', [XML])+len('</SUBJECT>')
                    ) as XML).value('(/SUBJECT/OPTION)[1]','nvarchar(2000)')
            FROM   dbo.Mails 
)) as SUBJECT

这个查询首先提取包含我转换为 XML 的电子邮件主题 (&lt;SUBJECT&gt;....&lt;/SUBJECT&gt;) 的字符串,然后使用 XML 的 value 函数获取主题值。

这可以正常工作,但在某些情况下 XML 格式不正确并且解析失败。示例:

DECLARE @XMLData XML =  '<SUBJECT>
      <OPTION CONSTRAINT="MASTER.IN_TITLE = ''  OR  MASTER.IN_LASTNAME = ''"><![CDATA[Découvrez nos offres de location]]>
      </OPTION>
      <OPTION CONSTRAINT="IN_TITLE <> '' AND MASTER.IN_LASTNAME <> ''"><![CDATA[~IN_TITLE~ ~IN_LASTNAME~, découvrez nos offres de location]]>
      </OPTION>
   </SUBJECT>'

select rtrim((@XMLData).value('(/SUBJECT/OPTION)[1]','nvarchar(2000)') )

Option 属性Constraint 中,我有一个特殊字符&lt;,如果我尝试转义这个字符,它会转义所有其他字符并且我丢失了XML 结构。那么如何逃脱呢?

另一个例子是这样的:

DECLARE @XMLData XML =  '<SUBJECT>
      <OPTION NAME="DEFAULT"><![CDATA[~(IF((IN_TITLE<>'' AND IN_LASTNAME<>''),IN_TITLE&' '&IN_LASTNAME&',',''))~ nos plus belles réalisations de 2015]]>
      </OPTION>
   </SUBJECT>'

select rtrim((@XMLData).value('(/SUBJECT/OPTION)[1]','nvarchar(2000)') )

这里我在&amp;IN_LASTNAME&amp; 附近遇到错误,但我认为我们不需要转义CDATA 中的特殊字符!

有没有人有办法避免这些解析错误?

【问题讨论】:

    标签: sql sql-server xml sql-server-2008 xml-parsing


    【解决方案1】:

    这些 XML 是如何生成的?这在你的控制之下吗?邪恶的三个字符“ 和& 必须特殊处理,CDATA 或转义。如果 XML 生成正确,它应该 不可能将它们放在禁止的地方...

    这里有两个工作示例。第二个与里斯琼斯相同...... 在第一个示例中,我将“”替换为&amp;lt;&amp;gt;

    顺便说一句:由于您显然正在处理其他特殊字符,因此您应该使用N'string' 标记您的字符串以将其读取为unicode

    DECLARE @XMLData XML =  REPLACE(N'<SUBJECT>
          <OPTION CONSTRAINT="MASTER.IN_TITLE = ''  OR  MASTER.IN_LASTNAME = ''"><![CDATA[Découvrez nos offres de location]]>
          </OPTION>
          <OPTION CONSTRAINT="IN_TITLE <> '' AND MASTER.IN_LASTNAME <> ''"><![CDATA[~IN_TITLE~ ~IN_LASTNAME~, découvrez nos offres de location]]>
          </OPTION>
       </SUBJECT>','<>','&lt;&gt;');
    
    select rtrim((@XMLData).value('(/SUBJECT/OPTION)[1]','nvarchar(2000)') );
    GO
    
    DECLARE @XMLData XML =  N'<SUBJECT>
          <OPTION NAME="DEFAULT"><![CDATA[~(IF((IN_TITLE<>'''' AND IN_LASTNAME<>''''),IN_TITLE&'' ''&IN_LASTNAME&'','',''''))~ nos plus belles réalisations de 2015]]>
          </OPTION>
       </SUBJECT>'
    
    select rtrim((@XMLData).value('(/SUBJECT/OPTION)[1]','nvarchar(2000)') )
    

    【讨论】:

      【解决方案2】:

      我花了一段时间才弄清楚这里出了什么问题 - 你的字符串中有单引号(第二个示例),它们需要加倍;

      DECLARE @XMLData XML =  '<SUBJECT>
            <OPTION NAME="DEFAULT"><![CDATA[~(IF((IN_TITLE<>'' AND IN_LASTNAME<>''),IN_TITLE&'' ''&IN_LASTNAME&'','',''))~ nos plus belles réalisations de 2015]]>
            </OPTION>
         </SUBJECT>'
      
      select rtrim((@XMLData).value('(/SUBJECT/OPTION)[1]','nvarchar(2000)') )
      

      【讨论】:

        【解决方案3】:

        “”字符组合中的额外“'。在我的示例中,我将其替换为“!=”。我意识到这可能不完全适合您的情况,因为您需要处理可能包含多行而不是变量的查询的结果,但希望这会给您一些想法。

        DECLARE @badStringPos int
        
        DECLARE @stringXMLData varchar(500)=  '<SUBJECT>
              <OPTION CONSTRAINT="MASTER.IN_TITLE = ''  OR  MASTER.IN_LASTNAME = ''"><![CDATA[Découvrez nos offres de location]]>
              </OPTION>
              <OPTION CONSTRAINT="IN_TITLE <> '' AND MASTER.IN_LASTNAME != ''"><![CDATA[~IN_TITLE~ ~IN_LASTNAME~, découvrez nos offres de location]]>
              </OPTION>
           </SUBJECT>'
        
        declare @XMLData xml
        
        set @badStringPos = patindex('%<>%', @stringXMLData)
        
        while @badStringPos <> 0
        begin
            set @stringXMLData = left(@stringXMLData, patindex('%<>%', @stringXMLData) - 1) + '!=' +
                                 right(@stringXMLData, len(@stringXMLData) - (patindex('%<>%', @stringXMLData) + 1))
            set @badStringPos = patindex('%<>%', @stringXMLData)
        end
        
        set @XMLData = convert(xml, @stringXMLData)
        
        select rtrim((@XMLData).value('(/SUBJECT/OPTION)[1]','nvarchar(2000)') )
        

        一种选择是使用游标遍历查询结果。

        【讨论】:

        • 您的想法原则上是正确的,但是将&amp;lt;&amp;gt; 替换为&amp;lt;&amp;gt; 更容易。优点是,您不会更改内容...
        • 感谢您的回答。替换 &amp;lt;&amp;gt; 完成了这项工作。但我只是按照@Shnugo 的建议使用了Replace 函数。
        • 哇。 @Shnugo 的回答要好得多。我想使用 REPLACE 功能,但 SSMS Intellisense/Tooltip 欺骗我认为我只能用 1 个其他字符替换 1 个字符。我应该如何解释“替换(表达式nvarchar(1),表达式nvarchar(1),表达式nvarchar(1))返回nvarchar(1)”?
        • 我应该检查一下 MSDN。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-09-05
        • 1970-01-01
        • 2021-11-21
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多