Hi all,
I recently had a project that involved getting data into and out of AX 2012 using Json strings and thought I'd blog it here. I know D365 is being used more and more, but thought it might still be useful for someone.
The requirement was to integrate various transactions with a 3rd party Retail package, so we exposed some methods to a web service hosted in AX (Inbound ports form). Reading elsewhere about AX 2012 not having lots of Json support built-in, we decided to use the free NewtonSoft Json.NET library.
Add the DLL as a reference in AX (after copying it to both server\bin and client\bin directories, or using the GAC).
Serialization
Serializing to a Json string works perfectly in AX by using a DataContractAttribute class to build the data. For example, if you need to send Customer information, you can create a class as follows:
[DataContractAttribute] public class CustMaster { CustAccount accountNum; Name firstName, lastName; PhoneLocal custPhone; Email custMail; } [DataMemberAttribute("Customer code")] public CustAccount parmCustAccountNum(CustAccount _accountNum = accountNum) { accountNum = _accountNum; return accountNum; } [DataMemberAttribute("First name")] public Name parmFirstName(Name _firstName = firstName) { firstName = _firstName; return firstName; } [DataMemberAttribute("Last name")] public Name parmLastName(Name _lastName = lastName) { lastName = _lastName; return lastName; } [DataMemberAttribute("Phone 1")] public PhoneLocal parmPhone(PhoneLocal _custPhone = custPhone) { custPhone = _custPhone; return custPhone; } [DataMemberAttribute("Email")] public Email parmEmail(Email _custMail = custMail) { custMail = _custMail; return custMail; }
The names corresponding to each variable, such as "Phone 1" are created as the tags in the Json string.
Using the above contract, we can create a Json string to send out by looping through the CustTable and adding each record to the string as follows:
[SysEntryPointAttribute] public str getCustomers() { CustTable cTable; str custStr = ''; System.Exception clrException; System.Object custObj; InteropPermission permission; System.Collections.ArrayList list = new System.Collections.ArrayList(); CustMaster custContract; try { permission = new InteropPermission(InteropKind::ClrInterop); permission.assert(); while select cTable { custContract = new CustMaster(); custContract.parmCustAccountNum(cTable.AccountNum); custContract.parmFirstName(cTable.Name()); custContract.parmLastName(cTable.Name()); custContract.parmPhone(cTable.phoneLocal()); custContract.parmEmail(cTable.email()); custObj = CLRInterop::getObjectForAnyType(custContract); list.Add(custObj); } if (list.get_Count() > 0) { custStr = Newtonsoft.Json.JsonConvert::SerializeObject(list, Newtonsoft.Json.Formatting::Indented); } CodeAccessPermission::revertAssert(); } catch (Exception::CLRError) { // BP deviation documented clrException = CLRInterop::getLastException(); if (clrException != null) { //BP Deviation Documented info(CLRInterop::getAnyTypeForObject(clrException.get_Message())); while (clrException != null) { clrException = clrException.get_InnerException(); if (clrException == null) break; checkFailed(CLRInterop::getAnyTypeForObject(clrException.ToString())); } } throw error("Failed"); }
The output will look like the following:
[ { "Customer code": "CUS001", "Email": "", "First name": "TEST CUSTOMER ONE", "Last name": "TEST CUSTOMER ONE", "Phone 1": "" }, { "Customer code": "CUS002", "Email": "", "First name": "TEST CUSTOMER TWO", "Last name": "TEST CUSTOMER TWO", "Phone 1": "" }, { "Customer code": "CUS003", "Email": "", "First name": "TEST CUSTOMER THREE", "Last name": "TEST CUSTOMER THREE", "Phone 1": "" } ]
Deserialization
For deserializing, it works slightly different. It doesn't seem like you can cast to an AX DataContractAttribute class from a .NET object (or at least I wasn't able to), so you will need to create a C# class and add that DLL as a reference in your AX environment as well. You can then call the Get_<parameterName>() methods directly from the .NET class.
Create the .NET class:
using System; using System.Collections; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Runtime.Serialization; namespace RetailClasses { [DataContract] public class CustOrders { [DataMember(Name = "CustomerAccount")] public string AccountNum { get; set; } [DataMember(Name = "CustomerReference")] public string CustReference { get; set; } [DataMember] public DateTime ShipmentDate { get; set; } [DataMember(Name = "SalesOrderLines")] public ArrayList OrderLines { get; set; } } [DataContract] public class CustOrderLines { [DataMember(Name = "ItemNumber")] public string ItemId { get; set; } [DataMember(Name = "Qty")] public float Quantity { get; set; } [DataMember(Name = "Colour")] public string ItemColour { get; set; } [DataMember(Name = "Size")] public string ItemSize { get; set; } } }
After adding this reference in AX, you can use the code as follows:
[SysEntryPointAttribute] public boolean createOrder(str _orderStr) { InteropPermission permission; System.Exception clrException; Newtonsoft.Json.Linq.JArray list = new Newtonsoft.Json.Linq.JArray(); Newtonsoft.Json.Linq.JObject jObject; System.Collections.IEnumerator lEnum; RetailClasses.CustOrders orders = new RetailClasses.CustOrders(); void processSalesQuote(RetailClasses.CustOrders _custOrders) { CustomerReference custRef; CustAccount custAccount; date shipDate; //SalesOrderLines; ItemId itemNumber; Qty qtyOrdered; EcoResItemColorName colour; EcoResItemSizeName size; RetailClasses.CustOrderLines orderLines = new RetailClasses.CustOrderLines(); System.Collections.ArrayList linesList = new System.Collections.ArrayList(); System.Collections.IEnumerator lEnum; Newtonsoft.Json.Linq.JObject jObject; custRef = _custOrders.get_CustReference(); custAccount = _custOrders.get_AccountNum(); shipDate = _custOrders.get_ShipmentDate(); linesList = _custOrders.get_OrderLines(); lEnum = linesList.GetEnumerator(); // Create Header ... // create lines while (lEnum.MoveNext()) { jObject = lEnum.get_Current(); orderLines = Newtonsoft.Json.JsonConvert::DeserializeObject(jObject.ToString(), orderLines.GetType()); size = orderLines.get_ItemSize(); colour = orderLines.get_ItemColour(); itemNumber = orderLines.get_ItemId(); qtyOrdered = orderLines.get_Quantity(); ... } } try { permission = new InteropPermission(InteropKind::ClrInterop); permission.assert(); list = Newtonsoft.Json.JsonConvert::DeserializeObject(_orderStr, list.GetType()); lEnum = list.GetEnumerator(); while (lEnum.MoveNext()) { jObject = lEnum.get_Current(); orders = Newtonsoft.Json.JsonConvert::DeserializeObject(jObject.ToString(), orders.GetType()); processSalesQuote(orders); } } catch (Exception::CLRError) { // BP deviation documented clrException = CLRInterop::getLastException(); if (clrException != null) { //BP Deviation Documented info(CLRInterop::getAnyTypeForObject(clrException.get_Message())); while (clrException != null) { clrException = clrException.get_InnerException(); if (clrException == null) break; checkFailed(CLRInterop::getAnyTypeForObject(clrException.ToString())); } } ret = checkFailed("Failed"); } }