gifts2017

Работаем с сервисом подсказок на формах: dadata.ru

Опубликовал Павел Алексеенко (qwinter) в раздел Программирование - Практика программирования

Рассмотрим: как можно использовать REST-сервис данного сайта для формирования списка выбора.

Публикация устарела в связи с изменениями API на сайте dadata.ru

 

Сервис dadata.ru позволяет получать подсказки по ФИО и по Адресам. У него есть довольно неплохо описанный API. Мы Рассмотрим, как с помощью него можно вывести подсказку в ФИО. 

Данными с сайтом можно обмениваться в двух форматах: 

  1. XML
  2. JSON

Я решил делать в xml, как в более используемом в 1С. Итак, для начала создадим новую пустую конфигурацию, со справочником Физлица, и так же создадим XDTO схему:

схема XDTO

Создадим форму элемента справочника Физлица.

Основной функцией у нас будет: ПолучитьОбъектXDTO, в которую мы будем передавать набираемый текст (Обратите внимание, что вы должны передавать в параметрах ваш код авторизации, который вы можете получить свободно на сайте).

&НаКлиенте
Функция ПолучитьОбъектXDTO(Текст, КоличествоПодсказок = 10)
	
	HTTPЗапрос = Новый HTTPЗапрос();
	HTTPЗапрос.АдресРесурса = "/api/v2/suggest/fio";
	
	HTTPЗапрос.Заголовки.Вставить("Content-Type", "application/xml");
	HTTPЗапрос.Заголовки.Вставить("Accept", "application/xml");
	HTTPЗапрос.Заголовки.Вставить("Authorization", "ВАШ КОД АВТОРИЗАЦИИ");
	HTTPЗапрос.УстановитьТелоИзСтроки("" + Текст + "" +  КоличествоПодсказок + "");
	
	Соединение = Новый HTTPСоединение("dadata.ru",,,,Новый ИнтернетПрокси,, Новый ЗащищенноеСоединениеOpenSSL);
	
	ОтветHTTP = Соединение.ОтправитьДляОбработки(HTTPЗапрос);
	
	Тело = ОтветHTTP.ПолучитьТелоКакСтроку();	
	Если Тело = "" Тогда
		Возврат Ложь;
	Иначе
		структурныйТип = ФабрикаXDTO.Тип("http://www.sample-package.org", "SuggestFioResponse"); 
		
		МойXML = Новый ЧтениеXML;
		МойXML.УстановитьСтроку(Тело);
		ОбъектXDTO = ФабрикаXDTO.ПрочитатьXML(МойXML, структурныйТип);
		
		Возврат ОбъектXDTO;
	КонецЕсли;
	
КонецФункции

Затем назначим элементу "Наименование" событие: "Автоподбор".

&НаКлиенте
Процедура НаименованиеАвтоПодбор(Элемент, Текст, ДанныеВыбора, Параметры, Ожидание, СтандартнаяОбработка)

	Если СтрДлина(Текст) > 3 Тогда

		Элемент.СписокВыбора.Очистить();	
		
		ОбъектXDTO  = ПолучитьОбъектXDTO(Текст);
		
		Если ТипЗнч(ОбъектXDTO) = Тип("ОбъектXDTO") Тогда
			Если ТипЗнч(ОбъектXDTO.suggestions) = Тип("СписокXDTO") Тогда
				Для каждого Ключ Из ОбъектXDTO.suggestions Цикл
					Элемент.СписокВыбора.Добавить(Ключ.value);	
				КонецЦикла;
			ИначеЕсли ТипЗнч(ОбъектXDTO.suggestions) = Тип("ОбъектXDTO") Тогда
				Элемент.СписокВыбора.Добавить(ОбъектXDTO.suggestions.value);	 
			КонецЕсли;
		КонецЕсли;
	КонецЕсли;
	
КонецПроцедуры

Можно пойти и дальше. Создадим у справочника реквизиты: Фамилия, Имя, Отчество и Пол (тип: Перечисления). И заполним их при окончании редактирования:

&НаКлиенте
Процедура НаименованиеПриИзменении(Элемент)
	
	Если ЗначениеЗаполнено(Объект.Наименование) Тогда
		ОбъектXDTO = ПолучитьОбъектXDTO(Объект.Наименование, 1);
		Если ТипЗнч(ОбъектXDTO) = Тип("ОбъектXDTO") Тогда
			ПолученныеДанные = ОбъектXDTO.suggestions;	 	
			Объект.Фамилия = ?(ТипЗнч(ПолученныеДанные.data.surname)=Тип("Строка"), ПолученныеДанные.data.surname, "");
			Объект.Имя = ?(ТипЗнч(ПолученныеДанные.data.name)=Тип("Строка"), ПолученныеДанные.data.name, "");
			Объект.Отчество = ?(ТипЗнч(ПолученныеДанные.data.patronymic)=Тип("Строка"), ПолученныеДанные.data.patronymic, "");
			
			Если ПолученныеДанные.data.gender = "MALE" Тогда
				Объект.Пол = ПредопределенноеЗначение("Перечисление.ПолФизическогоЛица.Мужской");	
			ИначеЕсли ПолученныеДанные.data.gender = "FEMALE" Тогда
				объект.Пол = ПредопределенноеЗначение("Перечисление.ПолФизическогоЛица.Женский");	
			КонецЕсли;
		КонецЕсли;
	КонецЕсли;
	
КонецПроцедуры

 Как результат получаем весьма удобный список подбора, и разложение на состовляющие ФИО.

результат

 оригинал

См. также

Подписаться Добавить вознаграждение

Комментарии

1. Евгений Шабалин (xzorkiix) 23.06.14 17:23
&НаСервере
Процедура УстановитьПол(gender)
	
	Если gender = "MALE" Тогда
		Объект.Пол = Перечисления.ПолФизическогоЛица.Мужской;	
	ИначеЕсли gender = "FEMALE" Тогда
		объект.Пол = Перечисления.ПолФизическогоЛица.Женский;	
	КонецЕсли;
	
КонецПроцедуры
...Показать Скрыть


Здесь достаточно на стороне клиента использовать ПредопределенноеЗначение("Перечисление.ПолФизическогоЛица.Женский");
2. Евгений Шабалин (xzorkiix) 23.06.14 17:29
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.УстановитьСтроку(СтрокаXML);
ОбъектXDTO = ФабрикаXDTO.ПрочитатьXML(ЧтениеXML);


wsСсылка, как таковая и не нужна для разбора ответа сервиса.
3. Павел Алексеенко (qwinter) 23.06.14 17:54
(2) xzorkiix, получается двойная работа, сначала создай wsСсылку и ее свойства, а затем еще надо присвоить им значения. Кода столько же как при HTTPЗапросе, плюс созданный объект в конфигурации. Вот в упор не вижу преимуществ при их использовании.
4. Евгений Шабалин (xzorkiix) 24.06.14 09:28
(3) qwinter, я хотел указать xdto пакет не нужен (оговорился). По крайней мере в рамках описанной задачи. wsСсылка о другом об обмене через протокол SOAP. В вашем случае при разборе ответа нет необходимости расписывать модель сервиса. Достаточно перед обращением к свойствам сервиса проверять их наличие.

Это если для разбора XML строки вы используете объекты XDTO

Функция Свойство(ОбъектXDTO, ИмяСвойства, Значение = Неопределено)
   Результат = Не (ОбъектXDTO.Свойства().Получить(ИмяСвойства) = Неопределено);
   Если Результат Тогда
      Значение = ОбъектXDTO[ИмяСвойства];
   КонецЕсли;
   Возварт Результат;
КонецФункции 

// . . .

Предложения = Неопределено;
Если Свойство(ОбъектXDTO, "suggestions", Предложения) Тогда
   // . . .
КонецЕсли;
...Показать Скрыть
theshadowco; qwinter; +2 Ответить 1
5. Павел Алексеенко (qwinter) 24.06.14 09:35
(4) xzorkiix, коментарии как обычно полезнее статьи)))
6. doxflow (bonv) 25.06.14 20:32
1.
&НаКлиенте
Функция ПолучитьОбъектXDTO(Текст, КоличествоПодсказок = 10)


Не делайте это на клиенте, в веб-клиенте работать не будет, в нем нет объекта XDTO. Лучше делать внеконтекстный вызов сервера.

2.
Тело = ОтветHTTP.ПолучитьТелоКакСтроку();	
Если Тело = "" Тогда


Это не совсем корректная проверка, если сервер вернет ошибку, то в теле будет ее описание.
Надо использовать проверку на код состояния

Если ОтветHTTP.КодСостояния = 200 Тогда


3.
структурныйТип = ФабрикаXDTO.Тип("http://www.sample-package.org", "SuggestFioResponse"); 
		
МойXML = Новый ЧтениеXML;
МойXML.УстановитьСтроку(Тело);
ОбъектXDTO = ФабрикаXDTO.ПрочитатьXML(МойXML, структурныйТип);
		
Возврат ОбъектXDTO;
...Показать Скрыть


Собственно определять в метаданных и затем использовать объект XDTO это лишнее.
Используете анонимный объект XDTO.

ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.УстановитьСтроку(Тело);
ЧтениеXML.ПерейтиКСодержимому();
ОбъектXDTO = Фабрика.ПрочитатьXML(ЧтениеXML);
ЧтениеXML.Закрыть();

Возврат ОбъектXDTO;
...Показать Скрыть


4.
&НаКлиенте
Процедура НаименованиеАвтоПодбор(Элемент, Текст, ДанныеВыбора, Параметры, Ожидание, СтандартнаяОбработка)


В этом методе лучше подключать обработчик ожидания, скажем на 0.5 сек и уже в нем заполнять список.
И опять же лучше отказаться от клиентского кода в пользу вызова внеконтекстного серверного вызова, из которого уже возвращать список.
awk; worker1c; +2 Ответить 1
7. Павел Алексеенко (qwinter) 25.06.14 20:56
8. Марина Чирина (chmv) 30.06.14 16:28
9. Александр Полтава (Патриот) 01.07.14 12:26
Более чем любопытно, плюсую, хороший пример демонстрации возможностей работы с интернет сервисом.Комментаторам тоже спасибо за дополнения, сейчас попробую реализовать.
10. Елена Журавлева (lyalius) 09.07.14 15:51
Отличная статья, только пример нерабочий.

Проблема вот в этих двух строках:

HTTPЗапрос.Заголовки.Вставить("Authorization", "ВАШ КОД АВТОРИЗАЦИИ");
HTTPЗапрос.УстановитьТелоИзСтроки("" + Текст + "" + КоличествоПодсказок + "");

Правильно так:

HTTPЗапрос.Заголовки.Вставить("Authorization", "Token ВАШ КОД АВТОРИЗАЦИИ");
HTTPЗапрос.УстановитьТелоИзСтроки("<req><query>" + Текст + "</query><count>" + КоличествоПодсказок + "</count></req>");

Еще момент: если по введенной пользователем строке ничего не будет найдено, то код сломается на строчке
> Если ТипЗнч(ОбъектXDTO.suggestions) = Тип("СписокXDTO") Тогда
потому что свойство suggestions в ответе отсутствует.
11. Александр Полтава (Патриот) 10.07.14 18:46
(10) lyalius, спасибо, так работает
12. Михаил Немчинов (mnemchinov) 12.07.14 18:06
У меня заработало только так:
HTTPЗапрос.УстановитьТелоИзСтроки("<req><query>" + Текст + "" + КоличествоПодсказок + "</query></req>");


И еще вместо:
Если ТипЗнч(ОбъектXDTO) = Тип("ОбъектXDTO") Тогда
		Если ТипЗнч(ОбъектXDTO.suggestions) = Тип("СписокXDTO") Тогда


сделал:
Если ТипЗнч(ОбъектXDTO) = Тип("ОбъектXDTO") Тогда
		Если ОбъектXDTO.Свойства().Получить("suggestions") <> Неопределено Тогда
			Если ТипЗнч(ОбъектXDTO.suggestions) = Тип("СписокXDTO") Тогда


и тогда окончательно заработало
olegovich; +1 Ответить
13. Павел Александрович (oslokot) 09.09.14 16:46
Всем привет! у этого сервиса, как я понимаю, сменилась версия api и теперь прежняя не работает.
Пытаюсь сделать как в примере, но сервер возвращает ошибку 400.
Текст ошибки: {"detail": "JSON parse error - No JSON object could be decoded"}
Странно, вроде все как надо сделал.

HTTPЗапрос = Новый HTTPЗапрос();
	HTTPЗапрос.АдресРесурса = "/api/v2/clean";
	HTTPЗапрос.Заголовки.Вставить("Content-Type", "application/json");
	HTTPЗапрос.Заголовки.Вставить("charset", "UTF-8");
	HTTPЗапрос.Заголовки.Вставить("Authorization", "Token API-ключ"); 
	HTTPЗапрос.Заголовки.Вставить("X-Secret", "Секретный ключ"); 
	Стр = "{ ""structure"": [ ""ADDRESS"" ], ""data"": [ [ ""Москва, Милютинский 13"" ] ] }";
	HTTPЗапрос.УстановитьТелоИзСтроки(Стр,КодировкаТекста.UTF8); 	
	Соединение = Новый HTTPСоединение("dadata.ru",,,,Новый ИнтернетПрокси,, Новый ЗащищенноеСоединениеOpenSSL);
	ОтветHTTP = Соединение.ОтправитьДляОбработки(HTTPЗапрос);
	
	Сообщить(ОтветHTTP.КодСостояния);
	Тело = ОтветHTTP.ПолучитьТелоКакСтроку();	
	Сообщить(Тело);
...Показать Скрыть

Может кто уже делал, подскажите пожалуйста где я наколбасил
14. Павел Александрович (oslokot) 09.09.14 17:44
(13) Сам себе и отвечу) Разобрался. Нужно перезаписать файл в кодировке UTF-8 без BOM и отправить серверу.
В общем неплохой сервис dadata.ru, жаль платный.
15. Антон (anton.fly7) 10.09.14 08:44
(14) покажите как вы устанавливаете тело НТТРЗапроса?
я делаю из строки, возвращает такую же ошибку
	ТекстЗапроса = "[ ""NAME"", ""PHONE"" ], ""data"": [ [ """ + Парам.Наименование + """, """ + Парам.Телефон + """ ] ]";
	
	ТекстЗапроса = "{ ""structure"": " + ТекстЗапроса + " }";
	
	HTTPЗапрос = Новый HTTPЗапрос();
	HTTPЗапрос.АдресРесурса = "/api/v2/clean";
	
	HTTPЗапрос.Заголовки.Вставить("Content-Type", "application/json");
	HTTPЗапрос.Заголовки.Вставить("Authorization", "Token " + Token); 
	HTTPЗапрос.Заголовки.Вставить("X-Secret", Secret);
	HTTPЗапрос.Заголовки.Вставить("charset", "UTF-8");
	HTTPЗапрос.УстановитьТелоИзСтроки(ТекстЗапроса);
...Показать Скрыть
16. Антон (anton.fly7) 10.09.14 09:01
(15) сам себе отвечу )

	ИмяВременногоФайла = ПолучитьИмяВременногоФайла();
	ЗТ = Новый ЗаписьТекста(ИмяВременногоФайла, КодировкаТекста.ANSI);
	ЗТ.Закрыть();
	ЗТ = Новый ЗаписьТекста(ИмяВременногоФайла,,, Истина, Символы.ПС);
	ЗТ.Записать(ТекстЗапроса);
	ЗТ.Закрыть();	
	
	HTTPЗапрос = Новый HTTPЗапрос();
	HTTPЗапрос.АдресРесурса = "/api/v2/clean";
	
	HTTPЗапрос.Заголовки.Вставить("Content-Type", "application/json");
	HTTPЗапрос.Заголовки.Вставить("Authorization", "Token " + Token); 
	HTTPЗапрос.Заголовки.Вставить("X-Secret", Secret);
	HTTPЗапрос.Заголовки.Вставить("charset", "UTF-8");
	//HTTPЗапрос.УстановитьТелоИзСтроки(ТекстЗапроса, КодировкаТекста.UTF8); 
	HTTPЗапрос.УстановитьТелоИзДвоичныхДанных(Новый ДвоичныеДанные(ИмяВременногоФайла));
...Показать Скрыть
progersan; +1 Ответить
17. Андрей Ромашов (Zixxx) 10.09.14 18:36
А что там по поводу SOAP?

По адресу: http://confluence.hflabs.ru/pages/viewpage.action?... существуют подсказки для протокола SOAP. Пробовал обратиться к wsdl по адресу: http://dadata.ru/suggestions/api/4_1/ws?wsdl, но в ответ получаю ошибку:

Error loading [http://dadata.ru/suggestions/api/4_1/ws?wsdl]: org.apache.xmlbeans.XmlException: org.apache.xmlbeans.XmlException: error: does not close tag

Я так понимаю что это неправильный адрес, но какой тогда правильный?
18. Елена Журавлева (lyalius) 17.09.14 17:05
(17) Zixxx, правильный вот этот https://dadata.ru/api/suggest. Та страница, на которую вы сослались — это чтобы вызвать подсказки, которые у вас внутри на сервере установлены.
19. Елена Журавлева (lyalius) 17.09.14 17:06
(14) oslokot, спасибо :) Посмотрите, у нас теперь и подсказки по ЕГРЮЛ есть.
20. Андрей Ромашов (Zixxx) 18.09.14 19:09
(18) lyalius, И где там по ссылке wsdl?
21. Павел Александрович (oslokot) 23.09.14 14:40
(19) lyalius, Да-да, видел.
Может как-нибудь воспользуюсь
Пока что использую свою обработку по данным контур.фокус

Ну а так, конечно молодцы, хорошая работа!
Желаю вам успешного развития проекта!
22. Елена Журавлева (lyalius) 23.09.14 17:45
(20) Zixxx, wsdl доступна тем, кто поставил сервис у себя в корпоративной сети. Вы не ставили и вам она не доступна. Вам нужно пользоваться вот этим сервисом: https://dadata.ru/api/suggest.
23. Елена Журавлева (lyalius) 23.09.14 17:46
(21) oslokot, спасибо, очень приятно слышать такие пожелания :)
Для написания сообщения необходимо авторизоваться
Прикрепить файл
Дополнительные параметры ответа