AXForum  
Вернуться   AXForum > Microsoft Dynamics AX > DAX: Администрирование
All
Забыли пароль?
Зарегистрироваться Правила Справка Пользователи Сообщения за день Поиск Все разделы прочитаны

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 15.07.2020, 17:55   #1  
mazzy is offline
mazzy
Участник
Аватар для mazzy
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
29,472 / 4494 (208) ++++++++++
Регистрация: 29.11.2001
Адрес: Москва
Записей в блоге: 10
ax2012, JSON для WCF Service: binding="webHttpBinding"?
Уважаемые знатоки сервисов, WCF и AIF в аксапте.
на аксфоруме было несколько обсуждений как в Аксапте работать с JSON запросами. обсуждения сводились к сериализации-десериализации json-строки средствами X++.

но точно известно, что:
1. сервисы в ax2012 построены на Майкрософтовском фреймворке WCF
2. WCF вполне работает с JSON на уровне протокола.
В частности, см. https://docs.microsoft.com/en-us/dot...without-aspnet

Другими словами, я ожидаю, что для Аксаптовских сервисов можно поменять настройку биндинга, например, на webHttpBinding и сам WCF будет разбираться с JSON или XML на основании заголовка content-type. А сервис внутри Аксапты получит вполне десериализованный объект (возможно со слабой типизацией).

Цитата:
HTTP POST requests with a content-type of "application/json" are treated as JSON, and those with content-type that indicate XML (for example, "text/xml") are treated as XML.
Вопрос 1: кто-нибудь копал в эту сторону? какие подводные камни?

Вопрос 2: behaviorConfiguration="serviceBehaviorConfiguration", "dataContractSerializer". Есть что почитать на эту тему? Расскажите как это работает в ax2012?

см. также:
https://docs.microsoft.com/en-us/dot.../nettcpbinding
https://docs.microsoft.com/en-us/dot...webhttpbinding
Миниатюры
Нажмите на изображение для увеличения
Название: 1.PNG
Просмотров: 163
Размер:	60.4 Кб
ID:	12896  
__________________
полезное на axForum, github, vk, coub.

Последний раз редактировалось mazzy; 15.07.2020 в 18:37.
За это сообщение автора поблагодарили: Raven Melancholic (5).
Старый 17.07.2020, 23:14   #2  
DSPIC is offline
DSPIC
Боец
 
1,077 / 1243 (44) ++++++++
Регистрация: 11.04.2008
есть в d365 класс formjsonserializer, вполне работоспособный и для ах2012. Сериализует контракт класс и обратно. А вот для 2009 пришлось потрудиться, т.к. не поддерживаются аттрибуты класса - но и это решаемо. В гугле написано, что эта фишка WCF там ещё в свой псевдо-объект заворачивает, так что врядли без кастылей сработает.
Старый 06.08.2020, 12:15   #3  
Brave is offline
Brave
Участник
 
26 / 14 (1) ++
Регистрация: 13.09.2010
Адрес: Санкт-Петербург
Цитата:
Сообщение от DSPIC Посмотреть сообщение
есть в d365 класс formjsonserializer, вполне работоспособный и для ах2012. Сериализует контракт класс и обратно.
Пробовал работать с jsonserializer в AX2012. Не всякий JSON им можно разобрать, особенно если в JSON имеются вложенные структуры с одинаковыми названиями JSONObject, но несущие в себе разные сущности. Конечно же проблема в лютой реализации некоторых JSON API с которыми приходилось работать.
Старый 06.08.2020, 13:05   #4  
Brave is offline
Brave
Участник
 
26 / 14 (1) ++
Регистрация: 13.09.2010
Адрес: Санкт-Петербург
Для полноценной работы с форматом JSON, используйте прослойку в виде ASP.NET WEB API прилложения, развёрнутого на IIS, которое занимается перегонкой JSON <-> WCF и объединением различных сервисов DAX, так же оно является промежуточным секюрным слоем, можно добавить кастомную систему авторизации, отфильтровать спам и кривые запросы, сообщить о недоступности сервисов DAX, обработать всякие ситуации, с которыми в DAX порой разобраться будет невозможно. Хорошим плюсом так же является возможность автоматического документирования web API сервиса (используем Swagger).
Стоит не забывать о том, что для работы с WCF DAX необходима авторизация. Для некоторых методов API используется стандартный QueryService, с возможностью постраничного запроса данных. Для некоторых моментов можно использовать метаданные AX так же через стандартный сервис MetadataService. В общем одни плюсы.
В целом всё получается достаточно красиво при правильной организации проекта, примеры вызова:
Код:
 /// <summary>
    /// Методы клиента (покупателя)
    /// </summary>
    [RoutePrefix("api/Sales/v1")]
    [ShowInSwagger]
    public class CustomersController : ApiController
    {
        private CallContext context = ClientFactory.CreateContext<CallContext>();

        /// <summary>
        /// Список клиентов
        /// </summary>
        /// <param name="page">Страница</param>
        /// <param name="pageSize">Количество записей на странице</param>
        [HttpGet]
        [Route("Customers")]
        [SwaggerResponse(HttpStatusCode.OK, Description = "Список клиентов", Type = typeof(IEnumerable<Models.Sales.Customer.CustAccount>))]
        [SwaggerResponse(HttpStatusCode.NotFound, Description = "По запросу ничего не найдено")]
        [SwaggerResponse(HttpStatusCode.BadRequest, Description = "Ошибка AIF/WCF", Type = typeof(AifFaultError))]
        [ResponseType(typeof(QueryResult))]
        [CommonExceptionFilters]
        public IHttpActionResult GetCustomers([FromUri] long page = 1, int pageSize = 50)
        {
            if (page <= 0 || pageSize <= 0)
                return BadRequest();

            return new Queries.Customer.CustomerListQuery(Request, new Models.Common.PaginationMetadata(page, pageSize));
        }
        /// <summary>
        /// Информация о клиенте
        /// </summary>
        /// <param name="CustAccount">Счёт (код клиента)</param>
        [HttpGet]
        [Route("Customers/{CustAccount}")]
        [SwaggerResponse(HttpStatusCode.OK, Description = "Информация о клиенте", Type = typeof(Models.Sales.Customer.CustomerInfo))]
        [SwaggerResponse(HttpStatusCode.NotFound, Description = "По запросу ничего не найдено")]
        [SwaggerResponse(HttpStatusCode.BadRequest, Description = "Ошибка AIF/WCF", Type = typeof(AifFaultError))]
        [CommonExceptionFilters]
        public async Task<IHttpActionResult> GetCustomerInfo([FromUri] string CustAccount)
        {
            Models.Sales.Customer.CustomerInfo result;

            using (var client = ClientFactory.CreateClient<WebCustServicesClient>())
            {
                var request = await client.GetCustomerAsync(context, CustAccount);

                if (request.response == null)
                    return NotFound();

                result = new Models.Sales.Customer.CustomerInfo
                {
                    CustAccount = request.response.CustAccount,
                    CustName = request.response.CustName,
                    WebEmail = request.response.WebEmail,
                    InventLocation = request.response.InventLocation,
                    PersonnelNumber = request.response.PersonnelNumber,
                    RouteId = request.response.RouteId,
                    SegmentId = request.response.SegmentId,
                    Settings = new Models.Sales.Customer.CustSettings() 
                    { 
                        AccountStatement            = request.response.CustSettings.AccountStatement.ToCustAccountStatement(),
                        SalesLineRejectionSendEmail = request.response.CustSettings.SalesLineRejectionSendEmail.ToNoYes()
                    }
                };
            }
            return Ok(result);
        }
        /// <summary>
        /// Баланс клиента
        /// </summary>
        /// <param name="CustAccount">Счёт (код клиента)</param>
        /// <param name="cur">Валюта</param>
        [HttpGet]
        [Route("Customers/{CustAccount}/Balance")]
        [SwaggerResponse(HttpStatusCode.OK, Description = "Баланс клиента", Type = typeof(Models.Sales.Customer.CustBalance))]
        [SwaggerResponse(HttpStatusCode.NotFound, Description = "По запросу ничего не найдено")]
        [SwaggerResponse(HttpStatusCode.BadRequest, Description = "Ошибка AIF/WCF", Type = typeof(AifFaultError))]
        [CommonExceptionFilters]
        public async Task<IHttpActionResult> GetCustBalance([FromUri] string CustAccount,string cur = "")
        {
            Models.Sales.Customer.CustBalance result;
 
            using (var client = ClientFactory.CreateClient<WebCustServicesClient>())
            {
                var request = await client.GetCustBalanceAsync(context, CustAccount,cur);

                result = new Models.Sales.Customer.CustBalance()
                {
                    CustAccount = request.response.CustAccount,
                    Currency = request.response.CustCurrencyCode,
                    Balance = request.response.Balance,
                    ReservAmount = request.response.ReservAmount,
                    SalesOpenAmount = request.response.SalesOpenAmount
                };
            }
            return Ok(result);
        }
....
Пример обработки ошибок WCF AIF:
Код:
 else if (exceptionType == typeof(ActionNotSupportedException))
            {
                AifFaultError error = new AifFaultError();
                error.Infolog.Add(new InfoMsg()
                {
                    Type = Enum.GetName(typeof(InfologMessageType), InfologMessageType.Error),
                    Message = "Метод WCF сервиса не поддерживается. Возможно он был изменён или переименован в AX."

                });
                status = HttpStatusCode.BadRequest;
                message = new ObjectContent<AifFaultError>(error, new JsonMediaTypeFormatter());
            }
            else
            {
                AifFaultError error = new AifFaultError();
                error.Infolog.Add(new InfoMsg()
                {
                    Type = Enum.GetName(typeof(InfologMessageType), InfologMessageType.Error),
                    Message = actionExecutedContext.Exception.Message
                });

                message = new ObjectContent<AifFaultError>(error, new JsonMediaTypeFormatter());
            }
Старый 21.10.2021, 11:12   #5  
Sergey Petrov is offline
Sergey Petrov
Участник
 
80 / 19 (1) ++
Регистрация: 03.04.2007
Адрес: Saint-Petersburg, Russia
Цитата:
Сообщение от DSPIC Посмотреть сообщение
есть в d365 класс formjsonserializer, вполне работоспособный и для ах2012. Сериализует контракт класс и обратно. А вот для 2009 пришлось потрудиться, т.к. не поддерживаются аттрибуты класса - но и это решаемо. В гугле написано, что эта фишка WCF там ещё в свой псевдо-объект заворачивает, так что врядли без кастылей сработает.
Коллеги, добрый день!
Не могли бы подробнее рассказать про методику использования этого функционала именно в DAX2009?
Проблема в том, что от внешнего API в DAX2009 получаем строку JSON-формата (формат фиксирован), парсим её и обрабатываем уже множество JSON-объектов. Причём объекты JSON могут быть вложены друг в друга неограниченное количество раз (при обработке объектов JSON используется рекурсия). Всё работает, но не быстро (особенно для крупных JSON-объектов). Возможна ли оптимизация за счёт создания структуры классов, соответствующих структуре получаемого JSON, и прямая десериализация?
__________________
MS Dynamics AX 2009

Kernel 5.0.1600.4110
Application 5.0.1500.6491
Старый 21.10.2021, 16:33   #6  
S.Kuskov is offline
S.Kuskov
Участник
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
 
3,438 / 1775 (66) ++++++++
Регистрация: 28.04.2007
Адрес: Калуга
Десериализации в X++ класс в версии AX2009 нет.
Но можно использовать .net и библиотеку Newtonsoft.Json
http://axgrind.azurewebsites.net/201...d-JSON-Parsing

Последний раз редактировалось S.Kuskov; 21.10.2021 в 16:38.
За это сообщение автора поблагодарили: Sergey Petrov (1).
Старый 22.10.2021, 01:13   #7  
DSPIC is offline
DSPIC
Боец
 
1,077 / 1243 (44) ++++++++
Регистрация: 11.04.2008
Вот готовый проект сериализации\десериализации JSON\XML, на базе Newtonsoft.Json. Попробуйте, сравните. Как раз реализуется концепция "за счёт создания структуры классов, соответствующих структуре получаемого JSON, и прямая десериализация". Но скорость обработки от этого не зависит.

Ремарки:
- В части JSON это в бОльшей степени даунгрейд класса FormJsonSerializer из D365
- В части XML написано с нуля
- Всё это реализовано в разгар событий августа 2020 в Беларуси, под шум взрывающихся гранат и стрельбы, поэтому есть огрехи и частности в коде. Вылизывать уже не было сил.
- Принцип использования как в AX2012\D365\.Net: создаём класс-контракт (можно вложенные), проставляем аттрибуты на методах, которые необходимо сериализоывать. Уровень вложенности значения не имеет.
- Вместо аттрибутов, которые AX2009 нативно не поддерживает, используются специального вида макрос, тут картинка

Вот так вызываем (в проекте есть джоб)
X++:
static void AXSerializerTutorial_JSON_XML(Args _args)
{
    VendTable           vendTable;
    DCVendor            dcVendor;
    DCVendors           dcVendors;

    Counter             idx;

    str                 json, xml;
    ;

    dcVendors = DCVendors::construct();

    while select vendTable
      order by RecId desc
    {
        dcVendor = DCVendor::constructVendTable(vendTable);

        dcVendors.parmVendors().addEnd(dcVendor);

        idx ++;

        if (idx == 3)
        {
            break;
        }
    }

    dcVendors.parmVendors(dcVendors.parmVendors()); // powinno być tak ;)

    json = dcVendors.serialize(HTTPRequestContentType::Json);

    xml  = dcVendors.serialize(HTTPRequestContentType::XML);

    info(json);
    info(xml);

    dcVendors = dcVendors.deserialize(json, HTTPRequestContentType::Json);
    // dcVendors = dcVendors.deserialize(xml,  HTTPRequestContentType::XML); // something went wrong... to be debugged
}
Получаем JSON
Нажмите на изображение для увеличения
Название: JSON1.png
Просмотров: 43
Размер:	60.0 Кб
ID:	13251

Получаем XML
Нажмите на изображение для увеличения
Название: XML1.png
Просмотров: 50
Размер:	61.5 Кб
ID:	13252


Да, прицел был больше на JSON, поэтому XMLная часть не особо отлаживалась - Я уже вижу на картинке не совсем верные наименования узлов + получил взлёт при десиарелизации. Наврное мелочь, но это не точно


В общем попробуйте - расскажите по скорости и в целом, как оно.

SharedProject_AXSerializer_JSON_XML.zip
За это сообщение автора поблагодарили: mazzy (5), trud (10), raz (5), sukhanchik (6), Ace of Database (10), vmoskalenko (6), Sergey Petrov (1), S.Kuskov (10).
Старый 23.10.2021, 17:18   #8  
Sergey Petrov is offline
Sergey Petrov
Участник
 
80 / 19 (1) ++
Регистрация: 03.04.2007
Адрес: Saint-Petersburg, Russia
Цитата:
Сообщение от S.Kuskov Посмотреть сообщение
Десериализации в X++ класс в версии AX2009 нет.
Но можно использовать .net и библиотеку Newtonsoft.Json
http://axgrind.azurewebsites.net/201...d-JSON-Parsing
Спасибо за ссылку. Попробовал реализовать чтение JSON по указанной логике. К сожалению, класс хорош для чтения данных из простых "одноуровневых" JSON без вложенности. У меня задача - разобрать JSON известного формата, но у которого уровень вложенности элементов друг в друга не ограничен. Можно реализовать рекурсию обработки уровней вложенности. Но получается жёсткая привязка к структуре конкретного JSON. Да и переход к несуществующему уровню генерит исключение, которое можно, конечно, обрабатывать, но как-то не хочется строить логику обработки на исключениях.

Пока остановился на обработке самих объектов JSON, которые предоставляет Newtonsoft.

Планирую ради эксперимента реализовать десериализацию и сравнить скорость при разборе JSON и при обработке структуры десериализованных классов.
__________________
MS Dynamics AX 2009

Kernel 5.0.1600.4110
Application 5.0.1500.6491
Старый 26.10.2021, 17:49   #9  
Sergey Petrov is offline
Sergey Petrov
Участник
 
80 / 19 (1) ++
Регистрация: 03.04.2007
Адрес: Saint-Petersburg, Russia
Цитата:
Сообщение от DSPIC Посмотреть сообщение
...В общем попробуйте - расскажите по скорости и в целом, как оно.
Прикрутил к нашей задаче. По времени почти то же самое. Чуть быстрее. Но структура и читабельность кода улучшилась на порядок. Так что, будем использовать предложенную методику.

Всем спасибо!
__________________
MS Dynamics AX 2009

Kernel 5.0.1600.4110
Application 5.0.1500.6491
Теги
aif, ax2012, json, services, wcf

 

Похожие темы
Тема Автор Раздел Ответов Посл. сообщение
atinkerersnotebook: Using Service Management to Track Service Orders Blog bot DAX Blogs 1 25.08.2013 19:16
AIF: Microsoft Dynamics AX Services and Windows Azure Service Bus Blog bot DAX Blogs 0 24.07.2013 03:13
emeadaxsupport: AX for Retail 2012 R2: Installing the Real-time Service Blog bot DAX Blogs 0 19.12.2012 11:11
Dynamics AX: WCF: The Enterprise Service Bus for Dynamics AX and the rest of the Microsoft Stack Blog bot DAX Blogs 0 10.03.2009 16:05
Pokluda: Outbound web service (AIF) Blog bot DAX Blogs 0 28.10.2006 17:43
Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск
Опции просмотра

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход

Рейтинг@Mail.ru
Часовой пояс GMT +3, время: 01:20.