Регистрация  |  Вход

Интеграция с Force.com. Используем XML в Apex коде (перевод)

В недавнем проекте мы столкнулись с задачей получения данных из XML, которые приходит со внешнего сервиса, и создания или обновления объектов Salesforce на основании этих данных. Внешний сервис использует REST API, который на вход принимает ExternalID и возвращает данные, связанные с данным ExternalID. В последующем мы используем эти данные для обновления записей на стороне Salesforce. В процессе разработки мы столкнулись с проблемой отсутствия подробной информации по работе с XML в Apex. В этой статье постараемся восполнить пробелы.



Вот пример ответа внешнего сервиса в формате XML, который нам необходимо обработать и вставить новые данные в объект Account.




<Accounts>
<Account>
<Type>Prospect</type>
<Industry>Manufacturing</industry>
<Employees>680</employees>
</Account>
<Account Name="Global media">
<Type>Prospect</type>
<Industry>Media</industry>
<Employees>14688</employees>
</Account>
</Accounts>


Чтобы получить эти данные с внешнего сервиса, необходимо подготовить запрос с описанием данных, которые нам необходимо получить. Для этой цели будем использовать externalID, который будет храниться в custom field AccountExternalID__c в объекте Account.



Порядок работы с внешним сервисом:



1. Создать запрос в формате XML, который будет содержать AccountExternalID__c и послать его на специальный URL внешнего сервиса.



2. Получить ответ в виде XML от внешнего сервиса и обработать его в Apex коде. Преобразовать XML в SObject и обновить записи в базе данных.




// функция SendRequest отсылает XML запрос на URL внешнего сервиса. Полученный ответ в RAW формате передается в функцию ReadResponse, которая осуществляет разбор XML в List<SObject>

public void sendRequest() {
// Получение списка Account, для которых необходимо получить данные с внешнего сервиса.
List < Account > accountList = new List < Account([Select AccountExternalID__с from Account limit 2]);

// Подготовка XML
XmlStreamWriter w = new XmlStreamWriter();
w.writeStartDocument(null, '1.0'); //Создание XML документа
w.writeStartElement(null, 'accounts', null);

//Цикл по записям Account, которые мы получили ранее, и подстановка данных в XML
for (Account a: accountList) {
w.writeStartElement(null, 'account', null);
w.writeStartElement(null, 'accountid', null);
w.writeCharacters(a.AccountExternalID__c);
w.writeEndElement();
w.writeEndElement();
}

w.writeEndElement();
w.writeEndDocument();

String xmlOutput = w.getXmlString();

w.close();

// Теперь у нас есть XML запрос, который мы пошлем на внешний сервис с помощью HttpRequest
System.HttpRequest request = new System.HttpRequest();
request.setEndpoint('https://integration.yourService.com/someEnpoint');
request.setHeader('Content-Type', 'application/xml');
request.setMethod('POST');
request.setBody(xmlOutput);

// Отправка запроса и получение ответа
System.HttpResponse response = new System.Http().send(request);
this.Response = response.getBody();

// Получение ответа в формате XML
XmlStreamReader reader = new XmlStreamReader(this.Response);
// Обработка полученного XML ответа
readResponse(reader);
}


Процесс обработки XML ответа выглядит следующим образом:



1. поиск элемента, оборачивающего необходимую информацию.



2. цикл по сложенным элементам для получения данных. Ответ от внешнего сервиса




<?xml version="1.0" encoding="UTF-8" ?>
<Accounts>
<Account>
<Type>Prospect</type>
<Industry>Manufacturing</industry>
<Employees>680</employees>
</Account>
<Account Name="Global media">
<Type>Prospect</type>
<Industry>Media</industry>
<Employees>14688</employees>
</Account>
</Accounts>


Вот код обработчика:




public void readResponse(XmlStreamReader reader) {
// Объект-буфер, в который будем собирать данные
Account accountRecord;
// List<Account> куда мы будем складывать созданные объекты
List < Account > accountList = new list < Account > ();
// Цикл по всем элементам XML документа
while (reader.hasNext()) {  
// Является ли текущий документ открывающимся тегом
if (reader.getEventType() == XmlTag.START_ELEMENT) {
// Является ли текущий тег тегом < Account >
if ('Account' == reader.getLocalName()) {
// Инициализируем AccountRecord
accountRecord = new Account();
} else if ('Type' == reader.getLocalName()) {
// Если тег является <Type>, то достаем данные
accountRecord.type = getValueFromTag(reader);
} else if ('Industry' == reader.getLocalName()) {
accountRecord.industry = getValueFromTag(reader);
} else if ('Employees' == reader.getLocalName()) {
accountRecord.employees = getValueFromTag(reader);
}
} else if (reader.getEventType() == XmlTag.END_ELEMENT) {
// Если текущий тег является закрывающим и является тегом <account>, то сохраняем объект-буфер
if ('Account' == reader.getLocalName()) {
accountList.add(accountRecord);
} else if ('Accounts' == reader.getLocalName()) {
// Если достигли конца документа, выходим
break;
}
}
}
}



// Это дополнительная функция для получения данных между тегами в виде String
public string getValueFromTag(XMLStreamReader reader) {
String DataValue;

while (reader.hasNext()) {
if (reader.getEventType() == XmlTag.END_ELEMENT) {
break;
} else if (reader.getEventType() == XmlTag.CHARACTERS) {
DataValue = reader.getText();
}
reader.next();
}

return DataValue;
}


Данный код выполняет пошаговое чтение XML документа, создание и заполнение объекта Account, и сохранение нового объекта в AccountList. источник (Siddesh Kabe)



конец перевода



Мое примечание. Взялся за перевод, особо не вникая в тонкости алгоритма и опираясь на авторитетность источника http://blogs.developerforce.com. Как оказалось зря. Я тоже сталкивался с аналогичной задачей на одном из своих проектов. Когда искал информацию и пути решения задачи разбора XML, способ, который предложил автор в статье выше отбросил. Есть способ разобрать XML на Apex более красивый и понятный. В следующей статье покажу как решил бы данную задачу я. То что написано здесь просто примите в качестве информации для саморазвития.