web
You’re offline. This is a read only version of the page.
close
Skip to main content

Notifications

Announcements

No record found.

Community site session details

Community site session details

Session Id :
Microsoft Dynamics AX (Archived)

Serializing and deserializing Json in AX

(2) ShareShare
ReportReport
Posted on by 303

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");
    }
}

*This post is locked for comments

I have the same question (0)
  • Ammar Salah Profile Picture
    150 on at

    I'm getting this error:

    Microsoft Dynamics anytype cannot be marshaled to CLR Object.

     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);
            }
  • Ammar Salah Profile Picture
    150 on at

    Hi guys, i have added a new class taken from D365 FFO and added to AX2012 with minor modifications, so you can easily pass any data contract (even nested) to serialize deserialize to JSON
    FIRST CREATE THIS CLASS

    /// <summary>

    /// Attribute to decorate a serializable collection property.

    /// </summary>

    public class SMCDataCollectionAttribute extends SysAttribute

    {

       Types _itemType;

       str _itemTypeName;

    }

    public Types itemType()

    {

       return _itemType;

    }

    public str itemTypeName()

    {

       return _itemTypeName;

    }

    public void new(Types itemType, str itemTypeName = '')

    {

       super();

     

       _itemType = itemType;

       _itemTypeName = itemTypeName;

    }

     THEN THIS CLASS

    /// <summary>

    /// taken from D365 finance and operations

    /// </summary>

    /// <remarks>

    /// AJS//

    /// </remarks>

     

    class SMCFormJsonSerializer

    {

       System.IO.StringWriter stringWriter;

       Newtonsoft.Json.JsonTextWriter jsonWriter;

       str nullPlaceholder;

       utcDateTime dateTimeBase;

       Map theDataContractClass;

       MapEnumerator theDataContractClassEnumerator;

    }

    /// <summary>

    /// flush the map object cache

    /// </summary>

    public void flushCache()

    {

       theDataContractClass = null;

    }

    private str json()

    {

       str json;

     

     

       json = stringWriter.ToString();

       json = strReplace(json, strFmt('"%1"', nullPlaceholder), 'null');

     

       return json;

    }

    private void new()

    {

       stringWriter = new System.IO.StringWriter();

       jsonWriter = new Newtonsoft.Json.JsonTextWriter(stringWriter);

       nullPlaceholder = guid2str(newGuid());

       dateTimeBase = SMCFormJsonSerializer::dateTimeBase();

    }

     

    private void serializeArray(Array _array)

    {

       Types arrayItemType;

       int arrayIndex;

     

       if (_array == null)

       {

           jsonWriter.WriteValue(nullPlaceholder);

           return;

       }

     

       jsonWriter.WriteStartArray();

     

       arrayItemType = _array.typeId();

       for(arrayIndex = 1; arrayIndex <= _array.lastIndex(); arrayIndex++)

       {

           if (arrayItemType == Types::Class)

           {

               this.serializeObject(_array.value(arrayIndex));

           }

           else

           {

               this.writePrimitiveValue(arrayItemType, _array.value(arrayIndex));

           }

       }

     

       jsonWriter.WriteEndArray();

    }

    private void serializeDataContract(Object _dataContract)

    {

       SysDictClass dictClass;

       DataContractAttribute dataContractAttribute;

       DataMemberAttribute dataMemberAttribute;

       Set methods;

       SetEnumerator se;

       SysDictMethod dictMethod;

       str dataMemberName;

       Object dataMemberValueObject;

       container dataMemberValuePrimitive;

       container memberValues;

       anytype memberMethodReturn;

       str memberName;

       str memberMethodName;

       ExecutePermission perm;

     

       Map theDataContractClassMember;

       MapEnumerator theDataContractClassMemberEnumerator;

       theDataContractClassMember = new Map(Types::String, Types::AnyType);

     

     

     

       if (theDataContractClass == null)

       {

           theDataContractClass = new Map(Types::string, Types::AnyType);

       }

     

       jsonWriter.WriteStartObject();

     

       dictClass = new SysDictClass(classIdGet(_dataContract));

     

       dataContractAttribute = dictClass.getAttribute(classStr(DataContractAttribute));

       if (dataContractAttribute == null)

       {

           throw error(strFmt("@SYS134831", dictClass.name(), dictClass.id()));

       }

     

       perm = new ExecutePermission();

       if(perm)

       {

           perm.assert();

       }

       // check if it exists if not create it

       if (!theDataContractClass.exists(dictClass.name()))

       {

           methods = dictClass.methods(true, false, true);

           se = methods.getEnumerator();

           while (se.moveNext())

           {

               dictMethod = se.current();

               dataMemberAttribute = dictMethod.getAttribute(classStr(DataMemberAttribute));

               if (dataMemberAttribute != null)

               {

                   dataMemberName = dataMemberAttribute.Name();

                    if (!dataMemberName)

                   {

                       dataMemberName = dictMethod.name();

                   }

                   memberValues = [dataMemberName, dictMethod.name(), dictMethod.returnType()];

                   [ memberName, memberMethodName, memberMethodReturn] = memberValues;

                   jsonWriter.WritePropertyName(memberName);

     

                   if (memberMethodReturn == Types::Class)

                   {

                       this.serializeObject(dictClass.callObject(memberMethodName, _dataContract));

                   }

                   else

                   {

                       this.writePrimitiveValue(memberMethodReturn, dictClass.callObject(memberMethodName, _dataContract));

                   }

               }

           }

     }

       CodeAccessPermission::revertAssert();

     

       jsonWriter.WriteEndObject();

    /*

       theDataContractClassMember = theDataContractClass.lookup(dictClass.name());

       theDataContractClassMemberEnumerator = new MapEnumerator(theDataContractClassMember);

       while (theDataContractClassMemberEnumerator.moveNext())

       {

           [ memberName, memberMethodName, memberMethodReturn] = theDataContractClassMemberEnumerator.currentValue();

           jsonWriter.WritePropertyName(memberName);

     

           if (memberMethodReturn == Types::Class)

           {

               this.serializeObject(dictClass.callObject(memberMethodName, _dataContract));

           }

           else

           {

               this.writePrimitiveValue(memberMethodReturn, dictClass.callObject(memberMethodName, _dataContract));

           }

       }

       jsonWriter.WriteEndObject();

    */

    }

    private void serializeList(List _list)

    {

       Types listItemType;

       ListEnumerator le;

     

     

       if (_list == null)

       {

           jsonWriter.WriteValue(nullPlaceholder);

           return;

       }

     

       jsonWriter.WriteStartArray();

     

       listItemType = _list.typeId();

       le = _list.getEnumerator();

       while (le.moveNext())

       {

           if (listItemType == Types::Class)

           {

               this.serializeObject(le.current());

           }

           else

           {

               this.writePrimitiveValue(listItemType, le.current());

           }

       }

     

       jsonWriter.WriteEndArray();

    }

    private void serializeObject(Object _object)

    {

       ClassId classId;

     

     

       if (_object == null)

       {

           jsonWriter.WriteValue(nullPlaceholder);

           return;

       }

     

       classId = classIdGet(_object);

     

       switch (classId)

       {

           case classNum(List):

               this.serializeList(_object);

               break;

     

           case classNum(Array):

               this.serializeArray(_object);

               break;

     

           default:

               this.serializeDataContract(_object);

               break;

       }

    }

    private void writeDateTimeValue(utcDateTime _value)

    {

       utcDateTime dateTimeValue;

       int64 jsDateTimeStamp;

       str jsonDateTime;

     

     

       dateTimeValue = _value;

     

     

       //jsonWriter.WriteValue(dateTimeValue);

     

       jsDateTimeStamp = DateTimeUtil::getDifference(dateTimeValue, dateTimeBase) * 1000;

       jsonDateTime = strFmt("\/Date(%1)\/", jsDateTimeStamp);

       jsonWriter.WriteValue(jsonDateTime);

    }

    private void writePrimitiveValue(Types valueType, anytype _value)

    {

       str strValue;

       int intValue;

       int64 int64Value;

       real realValue;

       enumId enumId;

       boolean boolValue;

       str guidValue;

       SysDictEnum dictEnum;

     

       if(!nullValue(_value))

       {

           valueType = typeOf(_value);

       }

     

       switch (valueType)

       {

           case Types::String:

             strValue = _value;

               jsonWriter.WriteValue(strValue);

               break;

     

           case Types::Integer:

               intValue = _value;

               jsonWriter.WriteValue(intValue);

               break;

     

           case Types::Int64:

               int64Value = _value;

               jsonWriter.WriteValue(int64Value);

               break;

     

           case Types::Real:

               realValue = _value;

               jsonWriter.WriteValue(realValue);

               break;

     

           case Types::UtcDateTime:

               this.writeDateTimeValue(_value);

               break;

     

           case Types::Date:

               this.writeDateTimeValue(DateTimeUtil::newDateTime(_value, 0));

               break;

     

           case Types::Enum:

               enumId = DictEnum::value2id(_value);

     

               if (enumId == enumNum(boolean))

               {

                   boolValue = _value;

                   jsonWriter.WriteValue(boolValue);

               }

               else

               {

                   dictEnum = new SysDictEnum(enumId);

                   if (dictEnum != null)

                   {

                       strValue = dictEnum.value2Symbol(_value);

                       jsonWriter.WriteValue(strValue);

                   }

                   else

                   {

                       throw error(strFmt("@SYS57821", enumId));

                   }

               }

     

               break;

     

           case Types::Guid:

               guidValue = guid2str(_value);

               jsonWriter.WriteValue(guidValue);

               break;

     

           default:

               throw error(strFmt("@SYS73815", valueType));

       }

    }

    private static utcdatetime dateTimeBase()

    {

       return DateTimeUtil::newDateTime(1\1\1970, 0);

    }

    /// <summary>

    /// Deserializes a collection of strongly typed items. The supported collection types are

    /// List and Array

    /// </summary>

    public static Object deserializeCollection(ClassId _collectionTypeId, str _serializedValue, Types _itemType, str _itemTypeName = '')

    {

       System.IO.StringReader stringReader;

       Newtonsoft.Json.JsonTextReader jsonReader;

       Object deserializedCollection = null;

     

       stringReader = new System.IO.StringReader(_serializedValue);

       jsonReader = new Newtonsoft.Json.JsonTextReader(stringReader);

       //jsonReader.DateParseHandling = Newtonsoft.Json.DateParseHandling::None;

     jsonReader.set_DateParseHandling(Newtonsoft.Json.DateParseHandling::None);

     

       while(deserializedCollection == null && jsonReader.Read())

       {

           //if (jsonReader.TokenType == Newtonsoft.Json.JsonToken::StartArray)

           if (jsonReader.get_TokenType() == Newtonsoft.Json.JsonToken::StartArray)

           {

               deserializedCollection = SMCFormJsonSerializer::deserializeCollectionInternal(_collectionTypeId, _itemType, _itemTypeName, jsonReader);

           }

       }

       return deserializedCollection;

    }

    /// <summary>

    /// Internal implementation of deserializing a collection of strongly typed items.

    /// Only few types of collections are supported

    /// </summary>

    private static Object deserializeCollectionInternal(ClassId _collectionTypeId, Types _itemType, str _itemTypeName, Newtonsoft.Json.JsonTextReader jsonReader)

    {

       Object deserializedCollection;

       List deserializedList;

       Array deserializedArray;

       anytype deserializedItem;

       boolean continueReading;

       int itemTypeId;

     

       // Instantiate the desired collection type

       if(_collectionTypeId == classnum(List))

       {

           deserializedList = new List(_itemType);

           deserializedCollection = deserializedList;

       }

       else if(_collectionTypeId == classnum(Array))

       {

           deserializedArray = new Array(_itemType);

           deserializedCollection = deserializedArray;

       }

     

       // Resolve the item type name to the right type ID

       if(_itemType == Types::Class)

       {

           itemTypeId = className2Id(_itemTypeName);

       }

       else if(_itemType == Types::Enum)

       {

           itemTypeId = enumName2Id(_itemTypeName);

       }

     

       continueReading = true;

       while(continueReading && jsonReader.Read())

       {

           deserializedItem = null;

           //if (jsonReader.TokenType == Newtonsoft.Json.JsonToken::EndArray)

           if (jsonReader.get_TokenType() == Newtonsoft.Json.JsonToken::EndArray)

           {

               // Reached the end of the current array

               continueReading = false;

           }

           //else if (jsonReader.TokenType == Newtonsoft.Json.JsonToken::StartObject)

           else if (jsonReader.get_TokenType() == Newtonsoft.Json.JsonToken::StartObject)

           {

               // Deserialize the object

               deserializedItem = SMCFormJsonSerializer::deserializeObjectInternal(itemTypeId, jsonReader);

           }

           /*

           else if (jsonReader.TokenType == Newtonsoft.Json.JsonToken::Boolean ||

               jsonReader.TokenType == Newtonsoft.Json.JsonToken::Date ||

               jsonReader.TokenType == Newtonsoft.Json.JsonToken::Float ||

               jsonReader.TokenType == Newtonsoft.Json.JsonToken::Integer ||

               jsonReader.TokenType == Newtonsoft.Json.JsonToken::String)

           */

           else if (jsonReader.get_TokenType() == Newtonsoft.Json.JsonToken::Boolean ||

               jsonReader.get_TokenType() == Newtonsoft.Json.JsonToken::Date ||

               jsonReader.get_TokenType() == Newtonsoft.Json.JsonToken::Float ||

               jsonReader.get_TokenType() == Newtonsoft.Json.JsonToken::Integer ||

               jsonReader.get_TokenType() == Newtonsoft.Json.JsonToken::String)

           {

               // Deserialize the basic type value

               //deserializedItem = SMCFormJsonSerializer::deserializeValue(_itemType, jsonReader.Value, itemTypeId);

               deserializedItem = SMCFormJsonSerializer::deserializeValue(_itemType, jsonReader.get_Value(), itemTypeId);

           }

     

           // If an item was deserialized, add to the collection

           if(deserializedItem != null)

           {

               if(deserializedList)

               {

                   // Add to the list

                   deserializedList.addEnd(deserializedItem);

               }

               else if(deserializedArray)

                {

                   // Add to the array

                   deserializedArray.value(deserializedArray.lastIndex() + 1, deserializedItem);

               }

           }

       }

       return deserializedCollection;

    }

    /// <summary>

    /// Deserializes an utcDateTime string.

    /// </summary>

    /// <param name = "_value">The string value to deserialize.</param>

    /// <returns>The deserialized date and time.</returns>

    public static utcdatetime deserializeDateTime(str _value)

    {

       //const str dateMatchStr = '/Date(';

       int dateValueStartPos;

       int dateExpressionEndPos;

       int64 dateValueMS;

       utcdatetime parsedDate;

       #define.dateMatchStr('/Date(')

     

       if (strScan(_value, #dateMatchStr, 1, strLen(_value)) == 1)

       {

           dateValueStartPos = strLen(#dateMatchStr) + 1;

           dateExpressionEndPos = strScan(_value, ')', 0, strLen(_value));

           if (dateExpressionEndPos > dateValueStartPos)

           {

               dateValueMS = str2Int64(subStr(_value, dateValueStartPos, dateExpressionEndPos - dateValueStartPos));

               return DateTimeUtil::addSeconds(SMCFormJsonSerializer::dateTimeBase(), dateValueMS div 1000);

           }

       }

     

       parsedDate = DateTimeUtil::parse(_value);

       return parsedDate;

    }

    /// <summary>

    /// Deserializes an object of a given type. Only the data-member attrbuted properties are deserialized.

    /// </summary>

    public static Object deserializeObject(ClassId _objectTypeId, str _serializedValue)

    {

       System.IO.StringReader stringReader;

       Newtonsoft.Json.JsonTextReader jsonReader;

       Object deserializedObject = null;

     

       stringReader = new System.IO.StringReader(_serializedValue);

       jsonReader = new Newtonsoft.Json.JsonTextReader(stringReader);

       //jsonReader.DateParseHandling = Newtonsoft.Json.DateParseHandling::None;

       jsonReader.set_DateParseHandling(Newtonsoft.Json.DateParseHandling::None);

     

       while(deserializedObject == null && jsonReader.Read())

       {

           //if(jsonReader.TokenType == Newtonsoft.Json.JsonToken::StartObject)

           if(jsonReader.get_TokenType() == Newtonsoft.Json.JsonToken::StartObject)

           {

               deserializedObject = SMCFormJsonSerializer::deserializeObjectInternal(_objectTypeId, jsonReader);

           }

       }

       return deserializedObject;

    }

    /// <summary>

    /// Internal implementatino of deserializing an object of a given type. Only the data-member attrbuted properties are deserialized.

    /// </summary>

    private static Object deserializeObjectInternal(ClassId _objectTypeId, Newtonsoft.Json.JsonTextReader jsonReader)

    {

       SysDictClass objectType = new SysDictClass(_objectTypeId);

       Set objectMethods;

       SysDictMethod objectMethod;

       SetEnumerator se;

       str currentJsonProperty;

       str dataMemberName;

       anytype propertyValue;

       DataMemberAttribute memberAttribute;

       //DataCollectionAttribute collectionAttribute;

       SMCDataCollectionAttribute collectionAttribute;

       Map dataMembers = new Map(Types::String, Types::Class);

       Object deserializedObject = objectType.makeObject();

       boolean continueReading;

       Types returnType;

       enumId enumId;

       DictType dt;

     

       // Find all valid data members on this type

       objectMethods = objectType.methods(true, false, true);

       se = objectMethods.getEnumerator();

       while (se.moveNext())

       {

           objectMethod = se.current();

           memberAttribute = objectMethod.getAttribute(classStr(DataMemberAttribute));

           if (memberAttribute != null)

           {

               dataMemberName = memberAttribute.Name();

               if (!dataMemberName)

               {

                   dataMemberName = objectMethod.name();

               }

               dataMembers.insert(dataMemberName, objectMethod);

           }

       }

     

       continueReading = true;

       while(continueReading && jsonReader.Read())

       {

           //if (jsonReader.TokenType == Newtonsoft.Json.JsonToken::EndObject)

           if (jsonReader.get_TokenType() == Newtonsoft.Json.JsonToken::EndObject)

           {

               // Reached the end of the current object

               continueReading = false;

           }

           //else if (jsonReader.TokenType == Newtonsoft.Json.JsonToken::PropertyName)

           else if (jsonReader.get_TokenType() == Newtonsoft.Json.JsonToken::PropertyName)

           {

              // This is identifying a property

               //currentJsonProperty = jsonReader.Value;

               currentJsonProperty = jsonReader.get_Value();

           }

           //else if (jsonReader.TokenType == Newtonsoft.Json.JsonToken::StartArray)

           else if (jsonReader.get_TokenType() == Newtonsoft.Json.JsonToken::StartArray)

           {

               // This is the case for collection type properties

               if(currentJsonProperty)

               {

                   if(dataMembers.exists(currentJsonProperty))

                   {

                       // Determine if the property has a colleciton attribute

                       objectMethod = dataMembers.lookup(currentJsonProperty);

                       collectionAttribute = objectMethod.getAttribute(classStr(SMCDataCollectionAttribute));

                       if(collectionAttribute)

                       {

                           // Deserialize the collection

                           propertyValue = SMCFormJsonSerializer::deserializeCollectionInternal(

                                objectMethod.returnId(),

                               collectionAttribute.itemType(),

                               collectionAttribute.itemTypeName(),

                               jsonReader);

                           // Set the property

                            objectType.callObject(objectMethod.name(), deserializedObject, propertyValue);

                       }

                   }

               }

           }

           //else if (jsonReader.TokenType == Newtonsoft.Json.JsonToken::StartObject)

           else if (jsonReader.get_TokenType() == Newtonsoft.Json.JsonToken::StartObject)

           {

               // This is the case for nested complex type properties

               if(currentJsonProperty)

               {

                   if(dataMembers.exists(currentJsonProperty))

                    {

                       // Read the object from JSON

                       objectMethod = dataMembers.lookup(currentJsonProperty);

                       // Desrialize the object property

                       propertyValue = SMCFormJsonSerializer::deserializeObjectInternal(objectMethod.returnId(), jsonReader);

                       // Set the proeprty value

                       objectType.callObject(objectMethod.name(), deserializedObject, propertyValue);

                   }

               }

           }

           /*

           else if (jsonReader.TokenType == Newtonsoft.Json.JsonToken::Boolean ||

               jsonReader.TokenType == Newtonsoft.Json.JsonToken::Date ||

               jsonReader.TokenType == Newtonsoft.Json.JsonToken::Float ||

               jsonReader.TokenType == Newtonsoft.Json.JsonToken::Integer ||

               jsonReader.TokenType == Newtonsoft.Json.JsonToken::String)

           */

           else if (jsonReader.get_TokenType() == Newtonsoft.Json.JsonToken::Boolean ||

               jsonReader.get_TokenType() == Newtonsoft.Json.JsonToken::Date ||

               jsonReader.get_TokenType() == Newtonsoft.Json.JsonToken::Float ||

               jsonReader.get_TokenType() == Newtonsoft.Json.JsonToken::Integer ||

               jsonReader.get_TokenType() == Newtonsoft.Json.JsonToken::String)

           {

               // This is the case for basic type values

               //if(jsonReader.Value && deserializedObject && currentJsonProperty)

               if(jsonReader.get_Value() && deserializedObject && currentJsonProperty)

              {

                   if(dataMembers.exists(currentJsonProperty))

                   {

                       objectMethod = dataMembers.lookup(currentJsonProperty);

                       //Types returnType = objectMethod.returnType();

                      returnType = objectMethod.returnType();

                       //enumId enumId = objectMethod.returnId();

                       enumId = objectMethod.returnId();

                       if (returnType == Types::UserType)

                       {

                          // Get the base type for extended data type

                           returnType = extendedTypeId2Type(objectMethod.returnId());

                           if (returnType == Types::Enum)

                           {

                               // Get the enumId for enum EDT types

                               //DictType dt = new DictType(objectMethod.returnId());

                               enumId = dt.enumId();

                           }

                       }

                       // Deserialize primitive value

                     propertyValue = SMCFormJsonSerializer::deserializeValue(

                           returnType,

                           //jsonReader.Value,

                           jsonReader.get_Value(),

                           enumId);

                       // Set the property value

                       objectType.callObject(objectMethod.name(), deserializedObject, propertyValue);

                   }

               }

           }

     

       }

       return deserializedObject;

    }

    /// <summary>

    /// Deserializes a primitive value from its string representation.

    /// </summary>

    public static anytype deserializeValue(Types _type, str _value, enumId _enum = 0)

    {

       anytype typedValue = null;

       int64 int64value;

       int intvalue;

       real realvalue;

     

       switch (_type)

       {

           case Types::Date:

               {

                   //iso date format to x++ date format

                   typedValue = str2Date(_value, 321); break;

               }

           case Types::Enum: typedValue = symbol2Enum(_enum, _value); break;

           case Types::Guid: typedValue = str2Guid(_value); break;

           case Types::Int64:

               {

                   //int64 int64value = System.Int64::Parse(_value, System.Globalization.CultureInfo::InvariantCulture);

                   int64value = System.Int64::Parse(_value, System.Globalization.CultureInfo::get_InvariantCulture());

                   typedValue = int64value;

                   break;

               }

           case Types::Integer:

               {

                   //int intvalue = System.Int32::Parse(_value, System.Globalization.CultureInfo::InvariantCulture);

                   intvalue = System.Int32::Parse(_value, System.Globalization.CultureInfo::get_InvariantCulture());

                   typedValue = intvalue;

                    break;

               }

           case Types::Real:

               {

                   //real realvalue = System.Decimal::Parse(_value, System.Globalization.CultureInfo::InvariantCulture);

                   realvalue = System.Decimal::Parse(_value, System.Globalization.CultureInfo::get_InvariantCulture());

                   typedValue = realvalue;

                   break;

               }

           case Types::String: typedValue = _value; break;

           case Types::VarString: typedValue = _value; break;

           case Types::Time: typedValue = str2time(_value); break;

           case Types::UtcDateTime: typedValue = SMCFormJsonSerializer::deserializeDateTime(_value); break;

           default:

               // Any other kind of type is illegal for setting

               throw error("The type of object cannot be set.");

       }

     

       return typedValue;

    }

    public static str normalizeNameForJSON(str inputString)

    {

       // this function must sync with NormalizeNameForJSON in %INETROOT%\Source\Kernel\Source\FormDataSourceInteraction.cpp

       int pos = strfind(inputString, '[', 1, strlen(inputString));

       if(pos > 0)

       {

           inputString = strDel(inputString, pos, 1);

           inputString = strIns(inputString, '_', pos);

     

           pos = strfind(inputString, ']', 1, strlen(inputString));

           if(pos > 0)

           {

               inputString = strDel(inputString, pos, 1);

           }

       }

       pos = strfind(inputString, ' ', 1, strlen(inputString));

       if(pos > 0)

       {

           inputString = strDel(inputString, pos, 1); // remove spaces in fieldname since they cause serialization error

       }

     

       return inputString;

    }

     

    public static str serializeClass(Object _object)

    {

       SMCFormJsonSerializer serializer;

     

       if (_object == null)

       {

           return 'null';

       }

     

       serializer = new SMCFormJsonSerializer();

       serializer.serializeObject(_object);

     

       return serializer.json();

    }

    public static str serializePrimitive(anytype propertyValue, enumId _enum = 0)

    {

       Types valueType;

       System.String strValue;

       System.Int32 intValue;

       System.Int64 int64Value;

       System.Decimal realValue;

       if(propertyValue != null)

       {

           valueType = typeOf(propertyValue);

       }

     

       switch (valueType)

       {

           case Types::Integer:

               intValue = propertyValue;

               //strValue = intValue.ToString(System.Globalization.CultureInfo::InvariantCulture);

               strValue = intValue.ToString(System.Globalization.CultureInfo::get_InvariantCulture());

               return strValue;

     

           case Types::Int64:

               int64Value = propertyValue;

               //strValue = int64Value.ToString(System.Globalization.CultureInfo::InvariantCulture);

               strValue = int64Value.ToString(System.Globalization.CultureInfo::get_InvariantCulture());

               return strValue;

     

           case Types::Real:

               realValue = propertyValue;

               //strValue = realValue.ToString(System.Globalization.CultureInfo::InvariantCulture);

               strValue = realValue.ToString(System.Globalization.CultureInfo::get_InvariantCulture());

               return strValue;

     

           case Types::Time:

               // x++ timeOfDay

               int64Value = propertyValue;

               //strValue = int64Value.ToString(System.Globalization.CultureInfo::InvariantCulture);

               strValue = int64Value.ToString(System.Globalization.CultureInfo::get_InvariantCulture());

               return strValue;

     

           case Types::UtcDateTime:

               //x++ utcdatetime format yyyy-mm-ddThh:mm:ss to iso datetime format

              return DateTimeUtil::toStr(propertyValue);

     

           case Types::Date:

               //x++ date format to iso date format yyyy-mm-dd

               strValue = date2str(propertyValue,321,DateDay::Digits2,DateSeparator::Hyphen,DateMonth::Digits2,DateSeparator::Hyphen,DateYear::Digits4);

               return strValue;

     

           case Types::Enum:

               if (_enum)

               {

                   return enum2Symbol(_enum, propertyValue);

               }

               else

               {

                   return strFmt('%1', propertyValue);

               }

     

           default:

               return strFmt('%1', propertyValue);

       }

     

    }

     

    AFTER ALL YOU CAN SERIALIZE TO JSON LIKE BELOW

    contract.parmArabicName(unitOfMeasure.Symbol);
    contract.parmMFQBusinessLine('2'); //always 2 for now
    contract.parmEnglishName(unitOfMeasure.Symbol);
    contract.parmIntegrationKey(unitOfMeasureDetails.SMCIntegrationKey);

    jsonMessageStr = SMCFormJsonSerializer::serializeClass(contract);

     

  • eldy.agustius Profile Picture
    5 on at

    Ammar, I had followed your suggestion as stated above and it works successfully. But the problem is how to define the order of the data contract member? Actually, I run your procedure several times and give different output order each time I ran. I had done the debugging and got this code to be the cause of the issue.

    methods = dictClass.methods(true, false, true);

    se = methods.getEnumerator();

    this code above is generating list of all the methods in a class. However, I did not found how or where I could change the code to follow the order such defined in SysOperationDisplayOrderAttribute in each of the data contract member. Could you please give me some enlightenment? Lot of thanks in advanced.

  • Ammar Salah Profile Picture
    150 on at

    Eldy, to be honest i didnt bother myself with  member order. Why would you need this anyway? if you are sending this to any API it will pass it doesnt need to be ordered.

  • Nirmal Raja Profile Picture
    4 on at

    Dear Ammar Salah,

    Thanks for your class.

    But when i try to deserialize a json with List, i am getting this error, any idea.

    Error executing code: List (object), method new called with invalid parameters.

    Stack trace

    (C)\Classes\List\new

    Thanks in advance.

  • Ammar Salah Profile Picture
    150 on at

    i have modified the class, i guess you have an old version. download it from here

    1drv.ms/.../s!Arp1ymoz-JngiHGK7TDZWvSG58M1

  • MUHAMMAD HAARIS Profile Picture
    770 on at

    Hi Ammar salah,

    I am using the same code which you have shared in one drive. But which method is need to be called primarily for deserializing the JSON string?

    Can you please share more details?

    Thanks in advance

  • Ammar Salah Profile Picture
    150 on at

    //deserialize JSON -> DC

       Object  deserializedContract;

       List    list;

    ResultClass  resultClass

               deserializedContract = SMCFormJsonSerializer::deserializeObject(classIdGet(resultClass), _jsonStr);

    resultClass = deserializedContract  as ResultClass  ;

    info(resultClass.parmResultCode());

    info(resultClass.parmResultMessage());

  • Somesh Profile Picture
    55 on at

    How to get the info result for multiple record under JSON.

  • Somesh Profile Picture
    55 on at

    Hi Ammar,

    I have checked that out, and for json like following:

    {

      "success": true,

      "data": [

          {

              "CUST_GROUP_ID": 1,

              "CUST_GROUP_NAME": "Customer Group 1",

              "INSERT_FLAG": "Y",

              "RECORD_SENT_FLAG": "N",

              "ERROR_MESSAGE": null,

              "LAST_UPDATE_DATE": "2020-03-01T13:29:29.000Z",

              "LAST_UPDATED_BY": 0,

              "CREATION_DATE": "2020-03-01T13:29:29.000Z",

              "CREATED_BY": 0

          },

          {

              "CUST_GROUP_ID": 2,

              "CUST_GROUP_NAME": "Customer Group 2",

              "INSERT_FLAG": "Y",

              "RECORD_SENT_FLAG": "N",

              "ERROR_MESSAGE": null,

              "LAST_UPDATE_DATE": "2020-03-01T13:29:29.000Z",

              "LAST_UPDATED_BY": 0,

              "CREATION_DATE": "2020-03-01T13:29:29.000Z",

              "CREATED_BY": 0

          }

      ]

    }

    I am trying to create a job with below code

    .

    RetailWebRequest            request;

       RetailWebResponse           response;

       str                         rawResponse;

       RetailCommonWebAPI          webApi;

       System.IO.Stream            requestStream, responseStream;

       System.IO.StreamReader      reader;

       Object                      deserializedContract;

       List                        list;

       ListIterator                listIterator;

       CreateCustGroupContract  custGroupContract;

       CustGroupList            custGroupList;

       webApi      = RetailCommonWebAPI::construct();

       request     = RetailWebRequest::newUrl("URL");

       response    = webApi.getResponse(request);

       rawResponse = response.parmData();

       custGroupList   = new CustGroupList();

       listIterator = new ListIterator(custGroupList.parmDataDetail());

       while (listIterator.more())

       {

           custGroupContract    = SMCFormJsonSerializer::deserializeObject(classIdGet(custGroupContract), listIterator.value());

           info(strFmt('%1', custGroupContract.parmCustGroupId()));

           info(custGroupContract.parmCustGroupName());

           listIterator.next();

       }

    but its only return "CUST_GROUP_ID": 1, "CUST_GROUP_NAME": "Customer Group 1".

    How can we achieve all the json string like "CUST_GROUP_ID": 1, "CUST_GROUP_NAME": "Customer Group 1", "CUST_GROUP_ID": 2, "CUST_GROUP_NAME": "Customer Group 2".

    Can you please help me to achieve the requirement.

    Thanks,

    Somesh

Under review

Thank you for your reply! To ensure a great experience for everyone, your content is awaiting approval by our Community Managers. Please check back later.

Helpful resources

Quick Links

Responsible AI policies

As AI tools become more common, we’re introducing a Responsible AI Use…

Neeraj Kumar – Community Spotlight

We are honored to recognize Neeraj Kumar as our Community Spotlight honoree for…

Leaderboard > 🔒一 Microsoft Dynamics AX (Archived)

#1
Martin Dráb Profile Picture

Martin Dráb 4 Most Valuable Professional

#1
Priya_K Profile Picture

Priya_K 4

#3
MyDynamicsNAV Profile Picture

MyDynamicsNAV 2

Last 30 days Overall leaderboard

Featured topics

Product updates

Dynamics 365 release plans