Использование IXMLDOMDocument и XPath для анализа XML-данных Dynamics 365 - PullRequest
0 голосов
/ 29 мая 2018

Продолжая мои другие вопросы о потреблении REST-сервисов из Dynamics 365, я обнаружил, что, установив Accept свойства TRESTRequest соответствующим образом, я могу получить результат запроса odata какXML (поскольку попытка анализа самого ответа на данные была просто разочаровывающим занятием в Delphi).

RESTRequest.Accept := 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8';

Приводит к этому результату (извините за тяжелый блок XML, но это то, с чем я имею дело, а не какой-нибудь приятный мелкий простой в использовании XML):

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<feed xml:base="https://mycrm.dynamics.com/XRMServices/2011/OrganizationData.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">BookableResourceSet</title>
  <id>https://mycrm.dynamics.com/XRMServices/2011/OrganizationData.svc/BookableResourceSet</id>
  <updated>2018-05-29T02:47:48Z</updated>
  <link rel="self" title="BookableResourceSet" href="BookableResourceSet" />
  <entry>
    <id>https://mycrm.dynamics.com/XRMServices/2011/OrganizationData.svc/BookableResourceSet(guid'f2060c99-0653-e811-8162-e0071b659e71')</id>
    <title type="text">John Smith</title>
    <updated>2018-05-29T02:47:48Z</updated>
    <author>
      <name />
    </author>
    <link rel="edit" title="BookableResource" href="BookableResourceSet(guid'f2060c99-0653-e811-8162-e0071b659e71')" />
    <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/systemuser_bookableresource_UserId" type="application/atom+xml;type=entry" title="systemuser_bookableresource_UserId" href="BookableResourceSet(guid'f2060c99-0653-e811-8162-e0071b659e71')/systemuser_bookableresource_UserId">
      <m:inline>
        <entry>
          <id>https://mycrm.dynamics.com/XRMServices/2011/OrganizationData.svc/SystemUserSet(guid'1648251b-9bff-e611-8107-c4346bc50748')</id>
          <title type="text">John Smith</title>
          <updated>2018-05-29T02:47:48Z</updated>
          <author>
            <name />
          </author>
          <link rel="edit" title="SystemUser" href="SystemUserSet(guid'1648251b-9bff-e611-8107-c4346bc50748')" />
          <category term="Microsoft.Crm.Sdk.Data.Services.SystemUser" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
          <content type="application/xml">
            <m:properties>
              <d:FirstName>John</d:FirstName>
              <d:TerritoryId m:type="Microsoft.Crm.Sdk.Data.Services.EntityReference">
                <d:Id m:type="Edm.Guid">c10a3010-0087-e711-8113-c4346bc540c4</d:Id>
                <d:LogicalName>territory</d:LogicalName>
                <d:Name>District 9</d:Name>
                <d:RowVersion m:null="true" />
              </d:TerritoryId>
              <d:int_StaffNumber>A111111</d:int_StaffNumber>
              <d:int_ServiceAreaId m:type="Microsoft.Crm.Sdk.Data.Services.EntityReference">
                <d:Id m:type="Edm.Guid" m:null="true" />
                <d:LogicalName m:null="true" />
                <d:Name m:null="true" />
                <d:RowVersion m:null="true" />
              </d:int_ServiceAreaId>
              <d:int_Title m:type="Microsoft.Crm.Sdk.Data.Services.OptionSetValue">
                <d:Value m:type="Edm.Int32" m:null="true" />
              </d:int_Title>
              <d:LastName>Smith</d:LastName>
            </m:properties>
          </content>
        </entry>
      </m:inline>
    </link>
    <category term="Microsoft.Crm.Sdk.Data.Services.BookableResource" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
    <content type="application/xml">
      <m:properties>
        <d:BookableResourceId m:type="Edm.Guid">f2060c99-0653-e811-8162-e0071b659e71</d:BookableResourceId>
        <d:int_cmsobjectid m:null="true" />
      </m:properties>
    </content>
  </entry>
  <entry>
    <id>https://mycrm.dynamics.com/XRMServices/2011/OrganizationData.svc/BookableResourceSet(guid'5f7767db-0653-e811-8162-e0071b659e71')</id>
    <title type="text">Joe Bloggs</title>
    <updated>2018-05-29T02:47:48Z</updated>
    <author>
      <name />
    </author>

Первоначально я сделал это:

  AXMLDoc := TXMLDocument.Create(nil);

  AXMLDoc.LoadFromXML(AResponseContent);
  AXMLDoc.Active := true;

  ARootNode := AXMLDoc.DocumentElement;

  APropertyNode := ARootNode.ChildNodes.FindNode('m:properties');

Или вариант, и рассмотрел цикл по всем узлам, чтобы найтите, которые я хотел, но функция FindNodes проверяет только непосредственные дочерние элементы, поэтому мне нужно написать рекурсивный метод, чтобы в основном выполнять поиск по всем дочерним узлам, пока я не найду тот, который мне нужен.Это привело меня к функции selectNodes , которая может сделать это для меня.

Тем не менее, это была игра с половиной, чтобы узнать, как даже использовать эту функцию, и послена нескольких примерах, которые не работали из-за устаревших методов, я наконец попробовал это:

  AXMLDOMDoc := TMSXMLDOMDocumentFactory.CreateDOMDocument;

  AXMLDOMDoc.loadXML(AResponseContent);

  AXMLNodeList := AXMLDOMDoc.selectNodes('/feed/entry/link/m:inline/entry/content/m:properties');

Но это дало мне "ссылку на необъявленный префикс пространства имен: m" error, что привело меня к SO / вопросу о настройке свойств документа DOM, поэтому я получил класс-потомок TMSXMLDOMDocumentFactory , чтобы не вызывать функцию selectNodes ошибка, гарантируя, что пространства имен включены.

  type
    TDynamics365XMLResponseDocument = class(TMSXMLDOMDocumentFactory)
    public
      class function CreateDOMDocument: IXMLDOMDocument; override;
    end;

  class function TDynamics365XMLResponseDocument.CreateDOMDocument: IXMLDOMDocument;
  begin
    Result := inherited;

    AddDOMProperty('SelectionNamespaces',
                   '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"');

    SetDOMProperties(Result as IXMLDOMDocument2);
  end;

Итак, что мне нужно, это IXMLDOMNodeList , содержащий все узлы "m: properties" , какименно они на самом деле содержат данные, которые я ищу.Чтобы добиться этого, я делаю то же, что и выше, но создаю документ из класса-потомка фабрики:

  // Because the XML back from Dynamics 365 contains namespaces, we need to specify the URI's for them as part
  // of the document factory

  AXMLDOMDoc := TDynamics365XMLResponseDocument.CreateDOMDocument;

  // Load the document with the raw XML

  AXMLDOMDoc.loadXML(AResponseContent);

  // Using a XPath query, get all the entry property nodes that have the data we need

  AXMLDOMNodeList := AXMLDOMDoc.selectNodes('//feed/entry/link/m:inline/entry/content/m:properties');

Я больше не получаю ошибки пространства имен, но AXMLDOMNodeList всегда пуст.Я попытался использовать несколько различных значений запроса, и все они возвращают список из 0 элементов, поэтому кажется, что selectNodes из IXMLDOMDocument не работает, и я могу 't зайдите в исходный код, чтобы понять, почему, поскольку интерфейсы только что сгенерированы из библиотеки типов Microsoft.

Может кто-нибудь определить, что я делаю неправильно?Или лучший способ сделать это?

...