Продолжая мои другие вопросы о потреблении 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.
Может кто-нибудь определить, что я делаю неправильно?Или лучший способ сделать это?