23.12.2021, 05:30 | #1 |
Участник
|
domhk: Taking JSON input with D365 service and deserialize into contract class
Источник: https://domhk.blogspot.com/2021/12/t...rvice-and.html
============== Recently I wanted to create a custom serivce in D365 taking in a JSON input. I did my usual google search but ended up needing additional trial and error to complete the use case. Here I'll share the solution I have.. Firstly, create a contract class which represent the a Json object /// /// The Student Information object contains the Student ID and Name /// [DataContractAttribute] class StudentInfoContract { str studentId; str studentName; [DataMemberAttribute("Student Id")] public str parmStudentId(str _studentId = studentId) { studentId = _studentId; return studentId; } [DataMemberAttribute("Student Name")] public str parmStudentName(str _studentName = studentName) { studentName = _studentName; return studentName; } } Then, in the service contract class, define a List of Str which will take in the Json /// /// Contains a list of students /// [DataContractAttribute] class StudentsContract { List studentList = new List(Types::String); [ DataMemberAttribute("Student list"), DataCollectionAttribute(Types::Class, classStr(StudentInfoContract)) ] public List parmStudentList(List _studentList = studentList) { if (!prmIsDefault(_studentList)) { studentList = _studentList; } return studentList; } } At run time, when calling code provides a list of Json objects, it'll be come a list of JObjects in X++. We'll loop through the list and use FormDeserializer to deserialize each JObject into a contract instance List studentList = StudentsContract.parmStudentList(); ListEnumerator enumerator = studentList.getEnumerator(); while (enumerator.moveNext()) { Newtonsoft.Json.Linq.JObject jObj = enumerator.current(); studentInfoContract = FormJsonSerializer::deserializeObject(classNum(StudentInfoContract),jObj.ToString()); str studentId, studentName; studentId = studentInfoContract.parmStudentId(); studentName = studentInfoContract.parmStudentName(); Info(strFmt("Studnet # %1: %2", studentId, studentName)); } In the end it's quite simple, and also no need to create C# class representing the data contract as well. This posting is provided "AS IS" with no warranties, and confers no rights. Источник: https://domhk.blogspot.com/2021/12/t...rvice-and.html |
|
24.12.2021, 12:00 | #2 |
Участник
|
Это называется "закат солнца вручную". В контрактах сервисов коллекции стоит "декорировать" атрибутом AifCollectionTypeAttribute - тогда и не придется их десериализовывать вручную.
X++: [ DataMemberAttribute("Student list"), AifCollectionTypeAttribute("return", Types::Class, classStr(StudentInfoContract)), AifCollectionTypeAttribute("_studentList", Types::Class, classStr(StudentInfoContract)) ] public List parmStudentList(List _studentList = studentList) Цитата:
Recently I wanted to create a custom serivce in D365 taking in a JSON input
Последний раз редактировалось gl00mie; 24.12.2021 в 12:03. |
|
|
За это сообщение автора поблагодарили: mazzy (2). |
24.12.2021, 13:41 | #3 |
Участник
|
пробелы в названии...
Какие заботливо разложенные грабли. |
|
29.12.2021, 15:20 | #4 |
Боец
|
Давайте разбираться.
Цитата:
Тут не вручную, а помощью стандартного класса FormJsonSerializer (D365). Во-вторых - задача, когда на вход извне впаривают сырой JSON (или XML) стоит сплошь и рядом: "Мы вам будем передавать XML\JSON строку и точка. Мы не можем переделать нашу систему под вашу, чтобы завязаться на ваш веб сервис. Или можем, но за большие деньги, где-то через года пол. Наши клиенты по всему миру, мы работаем только так". А какие есть альтернативы? Чаще всего для jSON встречается ручной парсинг строк strScan, strFind и т.д. Изредка попадаются продвинутые выринты - используют Newton.JSon.dll. C XML немного проще - стандартный класс пробега по XML с большего освоили, но это всё-равно абсолютный хардкод. Про аттрибут: Класс FormJsonSerializer понимает только DataCollectionAttribute. Более того, для дата-контрактов с их parm* методами этот аттрибут идеально подходит (на входе один параметр с таким же типом что и на выходе). AifCollectionTypeAttribute пришел из AX2012. Он больше подходит для описания сервис-операций, где может быть несколько входных параметров, разных типов, а на выходе что-то третье. Но в AX2012 других выриантов не было. Для сравнения: X++: // D365: [ DataMemberAttribute("Student list"), DataCollectionAttribute(Types::Class, classStr(StudentInfoContract)) ] public List parmStudentList(List _studentList = studentList) // ИЛИ AX2012: [ DataMemberAttribute("Student list"), AifCollectionTypeAttribute("return", Types::Class, classStr(StudentInfoContract)), AifCollectionTypeAttribute("_studentList", Types::Class, classStr(StudentInfoContract)) ] public List parmStudentList(List _studentList = studentList) Единственное - не уверен, понимает ли сериализатор D365 этот атрибут при описании контрактов для веб-сервисов - не приходилось применять, но как бы must Have. В XML так нельзя. В jSON можно. Претензия к автору блога у меня только одна: корее всего куски кода он где-то вырвал и не тестировал. Или спешил, промазал и недопроверил X++: while (enumerator.moveNext()) { Newtonsoft.Json.Linq.JObject jObj = enumerator.current(); studentInfoContract = FormJsonSerializer::deserializeObject(classNum(StudentInfoContract),jObj.ToString()); str studentId, studentName; studentId = studentInfoContract.parmStudentId(); studentName = studentInfoContract.parmStudentName(); Info(strFmt("Studnet # %1: %2", studentId, studentName)); } X++: Newtonsoft.Json.Linq.JObject jObj = enumerator.current(); Наверное он так хотел сделать: X++: public static void main(Args _args) { StudentsContract studentsContract; void addStudent(str _id, str _name) { StudentInfoContract student = new StudentInfoContract(); student.parmStudentId(_id); student.parmStudentName(_name); studentsContract.parmStudentList().addEnd(student); } studentsContract = new StudentsContract(); studentsContract.parmStudentList(new List(Types::Class)); addStudent("1", "First"); addStudent("2", "Second"); // Serialize str jSon = FormJsonSerializer::serializeClass(studentsContract); // Deserialize studentsContract = FormJsonSerializer::deserializeObject(classNum(StudentsContract), jSon); ListEnumerator enumerator = studentsContract.parmStudentList().getEnumerator(); while (enumerator.moveNext()) { StudentInfoContract student = enumerator.current(); info(strFmt("Studnet # %1: %2", student.parmStudentId(), student.parmStudentName())); } } Последний раз редактировалось DSPIC; 29.12.2021 в 15:25. |
|
|
За это сообщение автора поблагодарили: sukhanchik (4), vmoskalenko (6). |
29.12.2021, 22:57 | #5 |
Участник
|
Имя стандартного класса как бы намекает, что он задумывался больше для форм, чем для сервисов
Цитата:
Цитата:
Цитата:
X++: [DataMemberAttribute] public str parmDataSourceRecordsPacked(str _dataSourceRecordsPacked = '') { if (prmIsDefault(_dataSourceRecordsPacked)) { return SysOperationHelper::base64Encode(this.parmDataSourceRecordMapPacked()); } return SysOperationHelper::base64Encode(this.parmDataSourceRecordMapPacked(SysOperationHelper::base64Decode(_dataSourceRecordsPacked))); } Во-вторых, никто не мешал даже в AX2012 определить свой класс атрибута и парсить вручную что угодно аналогично тому, как это теперь делает FormJsonSerializer. Цитата:
Сообщение от DSPIC
Для сравнения:
X++: // D365: [ DataMemberAttribute("Student list"), DataCollectionAttribute(Types::Class, classStr(StudentInfoContract)) ] public List parmStudentList(List _studentList = studentList) // ИЛИ AX2012: [ DataMemberAttribute("Student list"), AifCollectionTypeAttribute("return", Types::Class, classStr(StudentInfoContract)), AifCollectionTypeAttribute("_studentList", Types::Class, classStr(StudentInfoContract)) ] public List parmStudentList(List _studentList = studentList) PHP код:
А для AifCollectionType мы получили StudentListAif в виде ArrayOfStudentInfoContract с полной разблюдовкой: мы видим в WSDL и сам StudentInfoContract, и то, что он состоит из двух строковых полей: StudentId и StudentName. И повторюсь: для AifCollectionType платформа занимается тем, что десериализует для вашего сервиса коллекцию объектов и заодно проверяет, что объекты в коллекции - нужного типа, а не абы что. По-моему, это всё в совокупности стоит того, чтобы добавить методу лишний атрибут. Последний раз редактировалось gl00mie; 29.12.2021 в 23:28. |
|
30.12.2021, 00:59 | #6 |
Боец
|
Оккей, давай по существу. Как будешь решать задачу десериализации?
Цитата:
Recently I wanted to create a custom service in D365 taking in a JSON input.
|
|
30.12.2021, 01:42 | #7 |
Banned
|
|
|
30.12.2021, 17:26 | #8 |
Участник
|
Цитата:
Платформа заботится о разработчике, беря на себя десериализацию и проверку корректности входного контракта сервиса - ей надо лишь чуть подсказать с помощью правильных атрибутов, если на входе ожидаются объекты-коллекции. Вообще вся тема, как мне лично представляется, не про вопрос выбора того или иного атрибута для поля-коллекции в контракте, не про то, что какие-то атрибуты кому-то нравятся больше, а кому-то - меньше, что от одних исходит аромат высоких стандартов разработки, а от других - затхлый запашок предыдущей версии системы, которая уже и с поддержки снята, и AIF-а вашего в новой версии нет, и вообще... Тема, по-моему, про то, сколько проблем и ручного труда можно поиметь, если использовать неправильный атрибут. Используйте правильный атрибут - и тратьте больше времени на бизнес-логику, а не на ковыряние в JSON-ах, парных квадратных скобках и форматах кодирования дат. Это всё уже давно решено в платформе, зачем изобретать велосипед?.. PS. С наступающим Новым годом |
|
30.12.2021, 19:57 | #9 |
Боец
|
Цитата:
Сообщение от Fattung
Taking JSON input with D365 service and deserialize into contract objects
Цитата:
Сообщение от Fattung
Recently I wanted to create a custom service in D365 taking in a JSON input.
Цитата:
Ок. А какую по-твоему задачу он решает? |
|
Теги |
d365fo, json, интеграция |
|
Опции темы | Поиск в этой теме |
Опции просмотра | |
|