Невозможно проанализировать XML с пространством имен - PullRequest
0 голосов
/ 09 июля 2019

Попытка разобрать XML из treasury.gov, и он использует два пространства имен. Тем не менее, я не могу извлечь данные из XML. Ничего не возвращается из моего запроса. Я попытался добавить пространства имен в подготовленный документ, но данные не возвращаются. Пространство имен - это проблема, как будто я их удаляю, мой код работает. В качестве обходного пути используется замена для устранения пространств имен для «очистки» XML. Это полностью исключает пространство имен, представляя прямой XML. Хотя это работает, это очень уродливо и, очевидно, не является правильным способом решения.

    DECLARE @xml AS XML;
    DECLARE @idoc INT;

SET @xml = '
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<feed xml:base="http://data.treasury.gov/Feed.svc/" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns="http://www.w3.org/2005/Atom">
  <title type="text">DailyTreasuryYieldCurveRateData</title>
  <id>http://data.treasury.gov/feed.svc/DailyTreasuryYieldCurveRateData</id>
  <updated>2019-05-30T20:27:58Z</updated>
  <link rel="self" title="DailyTreasuryYieldCurveRateData" href="DailyTreasuryYieldCurveRateData" />
  <entry>
    <id>http://data.treasury.gov/Feed.svc/DailyTreasuryYieldCurveRateData(1)</id>
    <title type="text"></title>
    <updated>2019-05-30T20:27:58Z</updated>
    <author>
      <name />
    </author>
    <link rel="edit" title="DailyTreasuryYieldCurveRateDatum" href="DailyTreasuryYieldCurveRateData(1)" />
    <category term="TreasuryDataWarehouseModel.DailyTreasuryYieldCurveRateDatum" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
    <content type="application/xml">
      <m:properties>
        <d:Id m:type="Edm.Int32">1</d:Id>
        <d:NEW_DATE m:type="Edm.DateTime">1997-01-02T00:00:00</d:NEW_DATE>
        <d:BC_1MONTH m:type="Edm.Double" m:null="true" />
        <d:BC_2MONTH m:type="Edm.Double" m:null="true" />
        <d:BC_3MONTH m:type="Edm.Double">5.190000057220459</d:BC_3MONTH>
        <d:BC_6MONTH m:type="Edm.Double">5.3499999046325684</d:BC_6MONTH>
        <d:BC_1YEAR m:type="Edm.Double">5.630000114440918</d:BC_1YEAR>
        <d:BC_2YEAR m:type="Edm.Double">5.96999979019165</d:BC_2YEAR>
        <d:BC_3YEAR m:type="Edm.Double">6.130000114440918</d:BC_3YEAR>
        <d:BC_5YEAR m:type="Edm.Double">6.3000001907348633</d:BC_5YEAR>
        <d:BC_7YEAR m:type="Edm.Double">6.4499998092651367</d:BC_7YEAR>
        <d:BC_10YEAR m:type="Edm.Double">6.5399999618530273</d:BC_10YEAR>
        <d:BC_20YEAR m:type="Edm.Double">6.8499999046325684</d:BC_20YEAR>
        <d:BC_30YEAR m:type="Edm.Double">6.75</d:BC_30YEAR>
        <d:BC_30YEARDISPLAY m:type="Edm.Double">0</d:BC_30YEARDISPLAY>
      </m:properties>
    </content>
  </entry>
</feed>
'

EXEC sp_XML_PrepareDocument @iDoc OUTPUT, @xml, '<feed xml:base="http://data.treasury.gov/Feed.svc/" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns:x="http://www.w3.org/2005/Atom">';

SELECT *
FROM OPENXML(@iDoc, 'feed/entry/content/m:properties/' )
WITH
(
   [Id] VARCHAR(100) 'd:Id'                                                                                             
  ,[Date] VARCHAR(100) 'd:NEW_DATE'
)


EXEC sp_xml_removedocument @iDoc

Столбцы Id и Date не возвращают данных. Если я устраню пространство имен, он вернет данные, как и ожидалось.

1 Ответ

0 голосов
/ 10 июля 2019

Подход с FROM OPENXML вместе с хранимыми процедурами для подготовки и удаления документа устарел и больше не должен использоваться. Скорее используйте родные методы XML:

Ваш XML

DECLARE @xml XML =
'<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<feed xml:base="http://data.treasury.gov/Feed.svc/" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns="http://www.w3.org/2005/Atom">
  <title type="text">DailyTreasuryYieldCurveRateData</title>
  <id>http://data.treasury.gov/feed.svc/DailyTreasuryYieldCurveRateData</id>
  <updated>2019-05-30T20:27:58Z</updated>
  <link rel="self" title="DailyTreasuryYieldCurveRateData" href="DailyTreasuryYieldCurveRateData" />
  <entry>
    <id>http://data.treasury.gov/Feed.svc/DailyTreasuryYieldCurveRateData(1)</id>
    <title type="text"></title>
    <updated>2019-05-30T20:27:58Z</updated>
    <author>
      <name />
    </author>
    <link rel="edit" title="DailyTreasuryYieldCurveRateDatum" href="DailyTreasuryYieldCurveRateData(1)" />
    <category term="TreasuryDataWarehouseModel.DailyTreasuryYieldCurveRateDatum" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
    <content type="application/xml">
      <m:properties>
        <d:Id m:type="Edm.Int32">1</d:Id>
        <d:NEW_DATE m:type="Edm.DateTime">1997-01-02T00:00:00</d:NEW_DATE>
        <d:BC_1MONTH m:type="Edm.Double" m:null="true" />
        <d:BC_2MONTH m:type="Edm.Double" m:null="true" />
        <d:BC_3MONTH m:type="Edm.Double">5.190000057220459</d:BC_3MONTH>
        <d:BC_6MONTH m:type="Edm.Double">5.3499999046325684</d:BC_6MONTH>
        <d:BC_1YEAR m:type="Edm.Double">5.630000114440918</d:BC_1YEAR>
        <d:BC_2YEAR m:type="Edm.Double">5.96999979019165</d:BC_2YEAR>
        <d:BC_3YEAR m:type="Edm.Double">6.130000114440918</d:BC_3YEAR>
        <d:BC_5YEAR m:type="Edm.Double">6.3000001907348633</d:BC_5YEAR>
        <d:BC_7YEAR m:type="Edm.Double">6.4499998092651367</d:BC_7YEAR>
        <d:BC_10YEAR m:type="Edm.Double">6.5399999618530273</d:BC_10YEAR>
        <d:BC_20YEAR m:type="Edm.Double">6.8499999046325684</d:BC_20YEAR>
        <d:BC_30YEAR m:type="Edm.Double">6.75</d:BC_30YEAR>
        <d:BC_30YEARDISPLAY m:type="Edm.Double">0</d:BC_30YEARDISPLAY>
      </m:properties>
    </content>
  </entry>
</feed>';

- Существует несколько пространств имен. Один xmlns - это так называемое пространство имен по умолчанию , в то время как другие пространства имен имеют префикс. Вы можете использовать любой префикс, который вам нравится, но - для удобства чтения - я использую то же, что и в оригинальном XML:

- В следующем коде будет продемонстрировано, как читать некоторые элементы верхнего уровня напрямую, как использовать .nodes() для погружения во вложенный элемент и как использовать другой .nodes() для погружения в относительный подузел.

WITH XMLNAMESPACES(DEFAULT 'http://www.w3.org/2005/Atom'
                          ,'http://schemas.microsoft.com/ado/2007/08/dataservices/metadata' AS m
                          ,'http://schemas.microsoft.com/ado/2007/08/dataservices' AS d
                          ,'http://data.treasury.gov/Feed.svc/' AS base )
SELECT @xml.value('(/feed/title/text())[1]','varchar(100)') AS title
      ,@xml.value('(/feed/title/@type)[1]','varchar(100)') AS title_type
      ,@xml.value('(/feed/id/text())[1]','varchar(100)') AS id
      ,@xml.value('(/feed/updated/text())[1]','datetime') AS updated --type-safe
      --pick more elements in top level
      ,ent.value('(id/text())[1]','varchar(100)') AS entry_id
      --pick more elements from entry-level
      ,prp.value('(d:Id/text())[1]','int') AS Prop_id
      --pick more elements below <m:properties>
FROM @xml.nodes('/feed/entry') A(ent)
CROSS APPLY A.ent.nodes('content/m:properties') B(prp);

- Если свойства могут быть неизвестны заранее, вы можете использовать * в пути и вернуть общий EAV-список:

WITH XMLNAMESPACES(DEFAULT 'http://www.w3.org/2005/Atom'
                          ,'http://schemas.microsoft.com/ado/2007/08/dataservices/metadata' AS m
                          ,'http://schemas.microsoft.com/ado/2007/08/dataservices' AS d
                          ,'http://data.treasury.gov/Feed.svc/' AS base )
SELECT @xml.value('(/feed/title/text())[1]','varchar(100)') AS title
      ,@xml.value('(/feed/title/@type)[1]','varchar(100)') AS title_type
      ,@xml.value('(/feed/id/text())[1]','varchar(100)') AS id
      ,@xml.value('(/feed/updated/text())[1]','datetime') AS updated --type-safe
      --pick more elements in top level
      ,ent.value('(id/text())[1]','varchar(100)') AS entry_id
      --pick more elements from entry-level
      ,AllPrps.value('local-name(.)','varchar(100)') AS Prop_Name
      ,AllPrps.value('@m:type','varchar(100)') AS Prop_Value
      ,AllPrps.value('text()[1]','varchar(100)') AS Prop_Value
      --pick more elements below <m:properties>
FROM @xml.nodes('/feed/entry') A(ent)
CROSS APPLY A.ent.nodes('content/m:properties/d:*') B(AllPrps)
...