|  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 | 
| Участник | 
			
			Имя стандартного класса как бы намекает, что он задумывался больше для форм, чем для сервисов   Цитата: Цитата:  Какая разница, откуда пришел атрибут? DataContractAttribute тоже был в AX2012, и что, может, на этом основании его не использовать?  Приложение D365FO на 90-95% - это приложение AX2012 R3, и если какие-то вещи из AX2012 продолжают успешно работать в D365FO, а альтернатива им - закат солнца вручную, то какой смысл от них отказываться? Цитата: 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 | 
| Участник | Цитата:  При разработке кастомного сервиса в D365FO нет задачи десериализации строки JSON на входе (хотя бы потому, что на входе может быть XML - если сервис будут дергать как SOAP). Нужно думать о том, чтобы разработать внятный контракт сервиса, а об остальном должны позаботиться платформа и клиент, вызывающий сервис. Если вы разработали контракт, в котором передается строка в JSON (ну мало ли - обстоятельства или контрагенты заставили), то это - совсем другая история. Я приводил штатный пример из FormLetterContract: на вход может подаваться строка, где в base64 закодирован бинарный контейнер, в который запакована коллекция записей произвольной таблицы. Ну и что это доказывает? Что все гланды на свете надо удалять через.. выхлопную трубу? Платформа заботится о разработчике, беря на себя десериализацию и проверку корректности входного контракта сервиса - ей надо лишь чуть подсказать с помощью правильных атрибутов, если на входе ожидаются объекты-коллекции. Вообще вся тема, как мне лично представляется, не про вопрос выбора того или иного атрибута для поля-коллекции в контракте, не про то, что какие-то атрибуты кому-то нравятся больше, а кому-то - меньше, что от одних исходит аромат высоких стандартов разработки, а от других - затхлый запашок предыдущей версии системы, которая уже и с поддержки снята, и 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, интеграция | 
|  | 
| 
 |