Json deserialize issue in service class

I’m not sure what i’m missing here.

Getting error as : {
“Message”: “An exception occured when deserializing the request - Exception occurred when parsing and deserializing parameter ‘record’ - ‘Error while deserializing contract class’”,
“ExceptionType”: “XppServicesDeserializationException”,
“ActivityId”: “9df60635-4832-0006-5d42-f69d3248da01”
}

Json:
{
“record”: [
{
“Header”: {
“SalesId”: “Test”,
“DeliveryDate”: “2024-01-01”
},
“Lines”: [
{
“ItemId”: “Test1”,
“Quantity”: 2
},
{
“ItemId”: “Test2”,
“Quantity”: 5
}
]
}
]
}

Code used:
public class ARTPackingSlipService extends SysOperationServiceBase
{
[AifCollectionType(‘record’, Types::Class, classStr(ARTPackingSlipContract))]
public str process(List record)
{
ARTPackingSlipContract contract = new ARTPackingSlipContract();
ARTPackingSlipHeaderContract headerContract = new ARTPackingSlipHeaderContract();
ARTPackingSlipLineContract lineContract = new ARTPackingSlipLineContract();

    PackingSlipId                   result;
    List                            errors = new List(Types::String);
    ListEnumerator                  headerle;
    ListEnumerator                  linele;
    List                            headerList;
    List                            lineList;
    SalesId                         salesId;
    TransDate                       deliveryDate;
    ItemId                          itemId;
    Qty                             qty;
    ListEnumerator                  enumerator = record.getEnumerator();

    while (enumerator.MoveNext())
    {
        contract = enumerator.current();

        try
        {
            headerList          = contract.parmHeader();
            headerle            = headerList.getEnumerator();

            while (headerle.moveNext())
            {
                headerContract  = headerle.current();
                salesId         = headerContract.parmSalesId();
                deliveryDate    = str2Date(strDel(headerContract.parmDeliveryDate(), 11, strLen(headerContract.parmDeliveryDate())),321);
            }

            lineList            = contract.parmLine();
            linele              = lineList.getEnumerator();

            while (linele.moveNext())
            {
                lineContract    = linele.current();
                itemId          = lineContract.parmItemId();
                qty             = lineContract.parmQuantity();
            }
        }
        catch
        {
        }
    }
    return "";
}

}

[DataContract]
class ARTPackingSlipContract
{
List header;
List line;

[
    DataMemberAttribute('Header'),
    DataCollectionAttribute(Types::Class, classStr(ARTPackingSlipHeaderContract))
]
public List parmHeader(List _header = header)
{
    header = _header;
    return header;
}

[
    DataMemberAttribute('Lines'),
    DataCollectionAttribute(Types::Class, classStr(ARTPackingSlipLineContract))
]
public List parmLine(List _line = line)
{
    line = _line;
    return line;
}

}

[DataContract]
class ARTPackingSlipHeaderContract
{
str salesId;
str deliveryDate;

[DataMember('SalesId')]
public str  parmSalesId(str _SalesId = salesId)
{
    salesId = _SalesId;
    return salesId;
}

[DataMember('DeliveryDate')]
public str  parmDeliveryDate(str _DeliveryDate = deliveryDate)
{
    deliveryDate = _DeliveryDate;
    return deliveryDate;
}

}

[DataContract]
class ARTPackingSlipLineContract
{
str itemId;
Qty qty;

[DataMember('ItemId')]
public str parmItemId(str _itemId = itemId)
{
    itemId = _itemId;
    return itemId;
}

[DataMember('Quantity')]
public Qty parmQuantity(Qty _qty = qty)
{
    qty = _qty;
    return qty;
}

}

Facing issue when trying to debug in VS, but before debug hit the VS in postman itself getting the error message.

Thanks in advance.

First of all, let me try to make your code easier to read.

JSON:

{
    “record”: [ 
        {
            “Header”: {
                “SalesId”: “Test”,
                “DeliveryDate”: “2024-01-01”
            },
            “Lines”: [
                {
                    “ItemId”: “Test1”,
                    “Quantity”: 2
                },
                {
                    “ItemId”: “Test2”,
                    “Quantity”: 5
                }
            ]
        }
    ]
}

X++

public class ARTPackingSlipService extends SysOperationServiceBase
{
    [AifCollectionType(‘record’, Types::Class, classStr(ARTPackingSlipContract))]
    public str process(List record)
    {
        ARTPackingSlipContract contract = new ARTPackingSlipContract();
        ARTPackingSlipHeaderContract headerContract = new ARTPackingSlipHeaderContract();
        ARTPackingSlipLineContract lineContract = new ARTPackingSlipLineContract();

        PackingSlipId                   result;
        List                            errors = new List(Types::String);
        ListEnumerator                  headerle;
        ListEnumerator                  linele;
        List                            headerList;
        List                            lineList;
        SalesId                         salesId;
        TransDate                       deliveryDate;
        ItemId                          itemId;
        Qty                             qty;
        ListEnumerator                  enumerator = record.getEnumerator();

        while (enumerator.MoveNext())
        {
            contract = enumerator.current();

            try
            {
                headerList          = contract.parmHeader();
                headerle            = headerList.getEnumerator();

                while (headerle.moveNext())
                {
                    headerContract  = headerle.current();
                    salesId         = headerContract.parmSalesId();
                    deliveryDate    = str2Date(strDel(headerContract.parmDeliveryDate(), 11, strLen(headerContract.parmDeliveryDate())),321);
                }

                lineList            = contract.parmLine();
                linele              = lineList.getEnumerator();

                while (linele.moveNext())
                {
                    lineContract    = linele.current();
                    itemId          = lineContract.parmItemId();
                    qty             = lineContract.parmQuantity();
                }
            }
            catch
            {
            }
        }
        return "";
    }
}

[DataContract]
class ARTPackingSlipContract
{
    List header;
    List line;

    [
        DataMemberAttribute('Header'),
        DataCollectionAttribute(Types::Class, classStr(ARTPackingSlipHeaderContract))
    ]
    public List parmHeader(List _header = header)
    {
        header = _header;
        return header;
    }

    [
        DataMemberAttribute('Lines'),
        DataCollectionAttribute(Types::Class, classStr(ARTPackingSlipLineContract))
    ]
    public List parmLine(List _line = line)
    {
        line = _line;
        return line;
    }
}

[DataContract]
class ARTPackingSlipHeaderContract
{
    str salesId;
    str deliveryDate;

    [DataMember('SalesId')]
    public str  parmSalesId(str _SalesId = salesId)
    {
        salesId = _SalesId;
        return salesId;
    }

    [DataMember('DeliveryDate')]
    public str  parmDeliveryDate(str _DeliveryDate = deliveryDate)
    {
        deliveryDate = _DeliveryDate;
        return deliveryDate;
    }
}

[DataContract]
class ARTPackingSlipLineContract
{
    str itemId;
    Qty qty;

    [DataMember('ItemId')]
    public str parmItemId(str _itemId = itemId)
    {
        itemId = _itemId;
        return itemId;
    }

    [DataMember('Quantity')]
    public Qty parmQuantity(Qty _qty = qty)
    {
        qty = _qty;
        return qty;
    }

}

Then please tell us whether you really have “ and ” instead of " in your JSON (which would be a problem), or is just something that changed incorrectly when pasting it to the forum.

Another problem I see that according to your X++ code, Header property contains an array of headeers, but your JSON does not. I’m assuming that having multiple headers doesn’t make a good sense, therefore the problem is in ARTPackingSlipContract class, where parmHeader() should accept and return an ARTPackingSlipHeaderContract object instead of a list.

when pasting it to forum " got changed as “ and ”.

Thanks for you valuable answer @MartinDrab

Based on your comments i made the changes in ‘ARTPackingSlipContract’ class and ‘ARTPackingSlipService’ class. Now its working fine i’m able to get the json values with a header and multiple lines in VS.

X++

[DataContract]
class ARTPackingSlipContract
{
    ARTPackingSlipHeaderContract header;
    List line;

    [
        DataMemberAttribute('Header'),
        DataCollectionAttribute(Types::Class, classStr(ARTPackingSlipHeaderContract))
    ]
    public ARTPackingSlipHeaderContract parmHeader(ARTPackingSlipHeaderContract _header = header)
    {
        header = _header;
        return header;
    }

    [
        DataMemberAttribute('Lines'),
        DataCollectionAttribute(Types::Class, classStr(ARTPackingSlipLineContract)),
        AifCollectionTypeAttribute('_line ', Types::Class, classStr(ARTPackingSlipLineContract)),
        AifCollectionTypeAttribute('return', Types::Class, classStr(ARTPackingSlipLineContract))
    ]
    public List parmLine(List _line = line)
    {
        line = _line;
        return line;
    }
}
public class ARTPackingSlipService extends SysOperationServiceBase
{
    [AifCollectionType('record', Types::Class, classStr(ARTPackingSlipContract))]
    public str process(List record)
    {
        ARTPackingSlipContract          contract = new ARTPackingSlipContract();
        ARTPackingSlipHeaderContract    headerContract = new ARTPackingSlipHeaderContract();
        ARTPackingSlipLineContract      lineContract = new ARTPackingSlipLineContract();
        PackingSlipId                   result;
        ListEnumerator                  headerle;
        ListEnumerator                  linele;
        List                            headerList;
        List                            lineList = new List(Types::Class);
        SalesId                         salesId;
        TransDate                       deliveryDate;
        ItemId                          itemId;
        Qty                             qty;
        ListEnumerator                  enumerator = record.getEnumerator();

        while (enumerator.MoveNext())
        {
            contract = enumerator.current();

            try
            {
                headerContract  = contract.parmHeader();
                salesId         = headerContract.parmSalesId();
                deliveryDate    = str2Date(strDel(headerContract.parmDeliveryDate(), 11, strLen(headerContract.parmDeliveryDate())),321);
                    
                lineList        = contract.parmLine();
                linele          = lineList.getEnumerator();

                while (linele.moveNext())
                {
                    lineContract    = linele.current();
                    itemId          = lineContract.parmItemId();
                    qty             = lineContract.parmQuantity();
                }
            }
            catch
            {
            }
        }
        return "";
    }
}

I am now facing a problem where my api key look like this

{
  "Content": [
    {
      "Id": 0,
      "CommercialOrderNo": "string",
      "MerchantOrderNo": "string",
      "MerchantComment": "string",
      "BillingAddress": {
        "Line1": "string",
        "Line2": "string",
      },
      "ShippingAddress": {
        "Line1": "string",
        "Line2": "string",
      },
      "TotalFee": 0,
      "Lines": [
        {
          "Id": 0,
          "Gtin": "string",
          "Description": "string",
          "StockLocation": {
            "Id": 0,
            "Name": "string"
          },
          "UnitVat": 0,

          "ExtraData": [
            {
              "Key": "string",
              "Value": "string"
            }
          ],
          "ChannelProductNo": "string",
          "Condition": "NEW",
          "LatestDeliveryDate": "2024-07-18T07:14:42.646Z",

        }
      ],
      "Phone": "string",
      "Email": "string",
      "ExtraData": {
        "additionalProp1": "string",
        "additionalProp2": "string",

      }
    }
  ],
  "Count": 0,
  "ExceptionType": "string",
  "ValidationErrors": {
    "additionalProp1": [
      "string"
    ],
    "additionalProp2": [
      "string"
    ],
  }
}

My question is how do i deal with the “content”, “Count”, “ExceptionType” and “ValidationErrors”. I see that in your example, u have used
[AifCollectionType(‘record’, Types::Class, classStr(ARTPackingSlipContract))] to deal with record. Should i write multiple AifCollectionType to deal with “content”, “Count”, “ExceptionType” and “ValidationErrors”?

Content is an array of objects. You need a contract class to define the structure of these objects.
Count is a number, not an object, therefore you don’t need a class for it.
ValidationErrors is an object (with two properties; arrays of strings).