Dynamics.IS JSON Mgt codeunit question (generating well formed JSON string)

jeighsohn
Member Posts: 15
Hello!
I am trying to implement the sample codeunit provided many years ago by Dynamics.is (https://dynamics.is/?p=2303) to modernize functionality we use in NAV2013. Currently, we are using a FedEx SOAP web service that is being deprecated by the host. They have created a RESTful API to replace the legacy SOAP connection, and since NAV2013 does not have native support for JSON, I used this forum (and google) to find the aforementioned codeunit.
I'm playing around with generating my JSON request first, and comparing it to the expected payload in the new API's documentation. I'm running into an issue I'm hoping someone can help me understand.
The expected payload features an array (streetLines shown below in both the shipper and recipient branches) within a branch within a branch.
I have used Gunnar's code example to get me close, but I don't understand how to generate the values in the streetLines array (there is no name:value pair, it's just the value, as the name is the array name). As a result, I get a malformed JSON string (it's adding another object with a null name and known value).
Can anyone direct me towards what I should be using here? This codeunit leverages the newtownsoft.dll and I'm wondering if I should be using a different class than WritePropertyName (null in my example above) and WriteValue found in the AddToJSon function within the custom codeunit?
Thanks!
Here is my C/AL code that I'm using to generate my JSON. It is very hard-coded for now as I get used to how the CU works.
I am trying to implement the sample codeunit provided many years ago by Dynamics.is (https://dynamics.is/?p=2303) to modernize functionality we use in NAV2013. Currently, we are using a FedEx SOAP web service that is being deprecated by the host. They have created a RESTful API to replace the legacy SOAP connection, and since NAV2013 does not have native support for JSON, I used this forum (and google) to find the aforementioned codeunit.
I'm playing around with generating my JSON request first, and comparing it to the expected payload in the new API's documentation. I'm running into an issue I'm hoping someone can help me understand.
The expected payload features an array (streetLines shown below in both the shipper and recipient branches) within a branch within a branch.
"requestedShipment": { "shipper": { "address": { "streetLines": [ "8181 Darrow Rd" ], "city": "Twinsburg", "stateOrProvinceCode": "OH", "postalCode": "44087", "countryCode": "US", "residential": false } }, "recipient": { "address": { "streetLines": [ "1550 Union Blvd", "Suite 302" ], "city": "Beverly Hills", "stateOrProvinceCode": "TN", "postalCode": "65247", "countryCode": "US", "residential": false } },
I have used Gunnar's code example to get me close, but I don't understand how to generate the values in the streetLines array (there is no name:value pair, it's just the value, as the name is the array name). As a result, I get a malformed JSON string (it's adding another object with a null name and known value).
"requestedShipment": { "shipper": { "address": { "streetLines": [ { "": "5543 Aliquet St" //This is wrong } ], "city": "Fort Dodge", "stateOrProvinceCode": "GA", "postalCode": "20783", "countryCode": "US", "residential": "false" } }, "recipient": { "address": { "streetLines": [ { "": "7292 Dictum Ave" //This is wrong } ], "city": "San Antonio", "stateOrProvinceCode": "MI", "postalCode": "47096", "countryCode": "US", "residential": "false" } } },
Can anyone direct me towards what I should be using here? This codeunit leverages the newtownsoft.dll and I'm wondering if I should be using a different class than WritePropertyName (null in my example above) and WriteValue found in the AddToJSon function within the custom codeunit?
Thanks!
Here is my C/AL code that I'm using to generate my JSON. It is very hard-coded for now as I get used to how the CU works.
WITH cduJSon DO BEGIN StartJSon; AddJSonBranch('accountNumber'); AddtoJSon('value','XXXXXXXXX'); EndJSonBranch; AddJSonBranch('rateRequestControlParameters'); AddtoJSon('returnTransitTimes',TRUE); AddtoJSon('servicesNeededOnRateFailure',FALSE); AddtoJSon('rateSortOrder','SERVICENAMETRADITIONAL'); ENDJSonBranch; AddJSonBranch('requestedShipment'); //Origin AddJSonBranch('shipper'); AddJSonBranch('address'); StartJSonArray('streetLines'); //TODO this result is malformed StartJson; AddtoJSon('','5543 Aliquet St'); //function requires name and value parameters, but name is already defined in the Array EndJSon; EndJSonArray; //streetLines AddtoJSon('city','Fort Dodge'); AddtoJSon('stateOrProvinceCode','GA'); AddtoJSon('postalCode','20783'); AddtoJSon('countryCode','US'); AddtoJSon('residential',FALSE); EndJSonBranch; //address EndJSonBranch; //shipper //Destination AddJSonBranch('recipient'); AddJSonBranch('address'); StartJSonArray('streetLines'); //TODO this result is malformed StartJSon; AddtoJSon('','7292 Dictum Ave'); //function requires name and value parameters, but name is already defined in the Array EndJSon; EndJSonArray; //streetLines AddtoJSon('city','San Antonio'); AddtoJSon('stateOrProvinceCode','MI'); AddtoJSon('postalCode','47096'); AddtoJSon('countryCode','US'); AddtoJSon('residential',FALSE); EndJSonBranch; //address EndJSonBranch; //recipient EndJSonBranch; //requestedShipment StartJSonArray('rateRequestType'); //TODO this result is malformed StartJSon; AddtoJSon('','ACCOUNT') //function requires name and value parameters, but name is already defined in the Array EndJSon; EndJSonArray; AddtoJSon('pickupType','USE_SCHEDULED_PICKUP'); StartJSonArray('requestedPackageLineItems'); StartJSon; AddJSonBranch('weight'); AddtoJSon('units','LB'); AddtoJSon('value',22); EndJSonBranch; //weight AddJSonBranch('dimensions'); AddtoJSon('length',10); AddtoJSon('width',8); AddtoJSon('height',4); AddtoJSon('units','IN'); EndJSonBranch; //dimensions; EndJSon; EndJSonArray; AddtoJSon('packagingType','YOUR_PACKAGING'); EndJSon; JSon := GetJSon;
0
Best Answers
-
Hello @jeighsohn,
You can add these 2 functions to the codeunit "Dynamics.is JSon Mgt."AddJSonBranch2(BranchName : Text) JsonTextWriter.WritePropertyName(BranchName);
The local variables in the AddArrayTxt2Json are::Name DataType Subtype Length JsonArray DotNet Newtonsoft.Json.Linq.JArray.'Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' JsonSerialize DotNet Newtonsoft.Json.JsonSerializer.'Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' ArrayValue Text i Integer
AddArrayTxt2Json(VariableName : Text;ArrayTxt : ARRAY [10] OF Text) ArrayValue := '['; FOR i := 1 TO ARRAYLEN(ArrayTxt) DO BEGIN IF ArrayTxt[i] <> '' THEN IF ArrayValue = '[' THEN ArrayValue := ArrayValue + '"' + ArrayTxt[i] + '"' ELSE ArrayValue := ArrayValue + ',' + '"' + ArrayTxt[i] + '"'; END; ArrayValue := ArrayValue + ']'; JsonArray := JsonArray.Parse(ArrayValue); JsonObj := JsonObj.JObject; JsonObj.Add(VariableName, JsonArray); JsonSerialize :=JsonSerialize.JsonSerializer; JsonSerialize.Serialize(JsonTextWriter, JsonObj);
And build the json like this:qArray[1] := '1550 Union Blv'; qArray[2] := 'Suite 302'; FunJson.StartJSon(); FunJson.AddJSonBranch('recipient'); FunJson.AddJSonBranch2('address'); FunJson.AddArrayTxt2Json('streetLines', qArray); // Not EndJSonBranch for address FunJson.EndJSonBranch(); // recipient FunJson.EndJSon; MESSAGE(FunJson.GetJSon());
The variables for the last code are:Name DataType Subtype Length FunJson Codeunit Dynamics.is JSon Mgt. qArray Text
Regards0 -
Hello @jeighsohn,
Assuming that the "rateRequestType" is like this"rateRequestType": ["ACCOUNT"],
You need to add a new function to the codeunit:AddArrayTxtProperty2Json(VariableName : Text;ArrayTxt : ARRAY [10] OF Text) ArrayValue := '['; FOR i := 1 TO ARRAYLEN(ArrayTxt) DO BEGIN IF ArrayTxt[i] <> '' THEN IF ArrayValue = '[' THEN ArrayValue := ArrayValue + '"' + ArrayTxt[i] + '"' ELSE ArrayValue := ArrayValue + ',' + '"' + ArrayTxt[i] + '"'; END; ArrayValue := ArrayValue + ']'; JsonArray := JsonArray.Parse(ArrayValue); JsonProperty := JsonProperty.JProperty(VariableName, JsonArray); JsonSerialize :=JsonSerialize.JsonSerializer; JsonSerialize.Serialize(JsonTextWriter, JsonProperty);
With these local variablesName DataType Subtype Length JsonArray DotNet Newtonsoft.Json.Linq.JArray.'Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' JsonSerialize DotNet Newtonsoft.Json.JsonSerializer.'Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' ArrayValue Text i Integer JsonProperty DotNet Newtonsoft.Json.Linq.JProperty.'Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed'
By the way I forgot to list the local variable JsonObj in the AddArrayTxt2Json function, so the complete local variables for that function are:Name DataType Subtype Length JsonObj DotNet Newtonsoft.Json.Linq.JObject.'Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' JsonArray DotNet Newtonsoft.Json.Linq.JArray.'Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' JsonSerialize DotNet Newtonsoft.Json.JsonSerializer.'Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' ArrayValue Text i Integer
So the JSON part of "requestedShipment" is:AddJSonBranch('requestedShipment'); //Origin AddJSonBranch('shipper'); AddJSonBranch2('address'); CLEAR(ArrayText); ArrayText[1] := '5543 Aliquet St'; ArrayText[2] := 'Ste B'; AddArrayTxt2Json('streetLines',ArrayText); AddToJSon('city','Fort Dodge'); AddToJSon('stateOrProvinceCode','GA'); AddToJSon('postalCode','20783'); AddToJSon('countryCode','US'); AddToJSon('residential',FALSE); EndJSonBranch; //shipper //Destination AddJSonBranch('recipient'); AddJSonBranch2('address'); CLEAR(ArrayText); ArrayText[1] := '7292 Dictum Ave'; ArrayText[2] := 'Unit 400'; AddArrayTxt2Json('streetLines',ArrayText); AddToJSon('city','San Antonio'); AddToJSon('stateOrProvinceCode','MI'); AddToJSon('postalCode','47096'); AddToJSon('countryCode','US'); AddToJSon('residential',FALSE); EndJSonBranch; //recipient CLEAR(ArrayText); ArrayText[1] := 'ACCOUNT'; AddArrayTxtProperty2Json('rateRequestType',ArrayText); //ERROR HAPPENS HERE AddToJSon('pickupType','USE_SCHEDULED_PICKUP'); EndJSonBranch; //requestedShipment
Regards0
Answers
-
Hello @jeighsohn,
You can add these 2 functions to the codeunit "Dynamics.is JSon Mgt."AddJSonBranch2(BranchName : Text) JsonTextWriter.WritePropertyName(BranchName);
The local variables in the AddArrayTxt2Json are::Name DataType Subtype Length JsonArray DotNet Newtonsoft.Json.Linq.JArray.'Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' JsonSerialize DotNet Newtonsoft.Json.JsonSerializer.'Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' ArrayValue Text i Integer
AddArrayTxt2Json(VariableName : Text;ArrayTxt : ARRAY [10] OF Text) ArrayValue := '['; FOR i := 1 TO ARRAYLEN(ArrayTxt) DO BEGIN IF ArrayTxt[i] <> '' THEN IF ArrayValue = '[' THEN ArrayValue := ArrayValue + '"' + ArrayTxt[i] + '"' ELSE ArrayValue := ArrayValue + ',' + '"' + ArrayTxt[i] + '"'; END; ArrayValue := ArrayValue + ']'; JsonArray := JsonArray.Parse(ArrayValue); JsonObj := JsonObj.JObject; JsonObj.Add(VariableName, JsonArray); JsonSerialize :=JsonSerialize.JsonSerializer; JsonSerialize.Serialize(JsonTextWriter, JsonObj);
And build the json like this:qArray[1] := '1550 Union Blv'; qArray[2] := 'Suite 302'; FunJson.StartJSon(); FunJson.AddJSonBranch('recipient'); FunJson.AddJSonBranch2('address'); FunJson.AddArrayTxt2Json('streetLines', qArray); // Not EndJSonBranch for address FunJson.EndJSonBranch(); // recipient FunJson.EndJSon; MESSAGE(FunJson.GetJSon());
The variables for the last code are:Name DataType Subtype Length FunJson Codeunit Dynamics.is JSon Mgt. qArray Text
Regards0 -
Thank you so much for the reply!
I was able to implement the recommended functions and get the desired output for the address line details!
Note, I also had this same problem later on in my payload, so I tried to implement the same type of solution there only this time I get an error.This message is for C/AL programmers: A call to Newtonsoft.Json.JsonSerializer.Serialize failed with this message: Token StartObject in state Object would result in an invalid JSON object. Path 'requestedShipment'. --------------------------- OK ---------------------------
This part of the payload is structured a little differently. This branch exists within the same overall requested shipment branch, but at one level of indentation. In the address example, the streetlines array was for the address branch within the shipper branch (for example), within the requested shipment. This raterequesttype array is a branch directly within the requested shipment. I don't see how to useWITH cduJSon DO BEGIN StartJSon; AddJSonBranch('accountNumber'); AddToJSon('value','XXXXXXXXX'); EndJSonBranch; AddJSonBranch('rateRequestControlParameters'); AddToJSon('returnTransitTimes',TRUE); AddToJSon('servicesNeededOnRateFailure',FALSE); AddToJSon('rateSortOrder','SERVICENAMETRADITIONAL'); EndJSonBranch; AddJSonBranch('requestedShipment'); //Origin AddJSonBranch('shipper'); AddJSonBranch2('address'); CLEAR(ArrayText); ArrayText[1] := '5543 Aliquet St'; ArrayText[2] := 'Ste B'; AddArrayTxt2Json('streetLines',ArrayText); AddToJSon('city','Fort Dodge'); AddToJSon('stateOrProvinceCode','GA'); AddToJSon('postalCode','20783'); AddToJSon('countryCode','US'); AddToJSon('residential',FALSE); EndJSonBranch; //shipper //Destination AddJSonBranch('recipient'); AddJSonBranch2('address'); CLEAR(ArrayText); ArrayText[1] := '7292 Dictum Ave'; ArrayText[2] := 'Unit 400'; AddArrayTxt2Json('streetLines',ArrayText); AddToJSon('city','San Antonio'); AddToJSon('stateOrProvinceCode','MI'); AddToJSon('postalCode','47096'); AddToJSon('countryCode','US'); AddToJSon('residential',FALSE); EndJSonBranch; //recipient CLEAR(ArrayText); ArrayText[1] := 'ACCOUNT'; AddArrayTxt2Json('rateRequestType',ArrayText); //ERROR HAPPENS HERE AddToJSon('pickupType','USE_SCHEDULED_PICKUP'); StartJSonArray('requestedPackageLineItems'); StartJSon; AddJSonBranch('weight'); AddToJSon('units','LB'); AddToJSon('value',22); EndJSonBranch; //weight AddJSonBranch('dimensions'); AddToJSon('length',10); AddToJSon('width',8); AddToJSon('height',4); AddToJSon('units','IN'); EndJSonBranch; //dimensions; EndJSon; EndJSonArray; AddToJSon('packagingType','YOUR_PACKAGING'); EndJSonBranch; //requestedShipment EndJSon; JSon := GetJSon; END;
0 -
Hello @jeighsohn,
Could you post a complete JSON example that you try to create or at least the "rateRequestType" part?
Regards.0 -
Hello @jeighsohn,
Assuming that the "rateRequestType" is like this"rateRequestType": ["ACCOUNT"],
You need to add a new function to the codeunit:AddArrayTxtProperty2Json(VariableName : Text;ArrayTxt : ARRAY [10] OF Text) ArrayValue := '['; FOR i := 1 TO ARRAYLEN(ArrayTxt) DO BEGIN IF ArrayTxt[i] <> '' THEN IF ArrayValue = '[' THEN ArrayValue := ArrayValue + '"' + ArrayTxt[i] + '"' ELSE ArrayValue := ArrayValue + ',' + '"' + ArrayTxt[i] + '"'; END; ArrayValue := ArrayValue + ']'; JsonArray := JsonArray.Parse(ArrayValue); JsonProperty := JsonProperty.JProperty(VariableName, JsonArray); JsonSerialize :=JsonSerialize.JsonSerializer; JsonSerialize.Serialize(JsonTextWriter, JsonProperty);
With these local variablesName DataType Subtype Length JsonArray DotNet Newtonsoft.Json.Linq.JArray.'Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' JsonSerialize DotNet Newtonsoft.Json.JsonSerializer.'Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' ArrayValue Text i Integer JsonProperty DotNet Newtonsoft.Json.Linq.JProperty.'Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed'
By the way I forgot to list the local variable JsonObj in the AddArrayTxt2Json function, so the complete local variables for that function are:Name DataType Subtype Length JsonObj DotNet Newtonsoft.Json.Linq.JObject.'Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' JsonArray DotNet Newtonsoft.Json.Linq.JArray.'Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' JsonSerialize DotNet Newtonsoft.Json.JsonSerializer.'Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' ArrayValue Text i Integer
So the JSON part of "requestedShipment" is:AddJSonBranch('requestedShipment'); //Origin AddJSonBranch('shipper'); AddJSonBranch2('address'); CLEAR(ArrayText); ArrayText[1] := '5543 Aliquet St'; ArrayText[2] := 'Ste B'; AddArrayTxt2Json('streetLines',ArrayText); AddToJSon('city','Fort Dodge'); AddToJSon('stateOrProvinceCode','GA'); AddToJSon('postalCode','20783'); AddToJSon('countryCode','US'); AddToJSon('residential',FALSE); EndJSonBranch; //shipper //Destination AddJSonBranch('recipient'); AddJSonBranch2('address'); CLEAR(ArrayText); ArrayText[1] := '7292 Dictum Ave'; ArrayText[2] := 'Unit 400'; AddArrayTxt2Json('streetLines',ArrayText); AddToJSon('city','San Antonio'); AddToJSon('stateOrProvinceCode','MI'); AddToJSon('postalCode','47096'); AddToJSon('countryCode','US'); AddToJSon('residential',FALSE); EndJSonBranch; //recipient CLEAR(ArrayText); ArrayText[1] := 'ACCOUNT'; AddArrayTxtProperty2Json('rateRequestType',ArrayText); //ERROR HAPPENS HERE AddToJSon('pickupType','USE_SCHEDULED_PICKUP'); EndJSonBranch; //requestedShipment
Regards0 -
Hello @jeighsohn,
I think that you can use only that last function (AddArrayTxtProperty2Json) and the code should be this:AddJSonBranch('requestedShipment'); //Origin AddJSonBranch('shipper'); AddJSonBranch('address'); CLEAR(ArrayText); ArrayText[1] := '5543 Aliquet St'; ArrayText[2] := 'Ste B'; AddArrayTxtProperty2Json('streetLines',ArrayText); EndJSonBranch; // address AddToJSon('city','Fort Dodge'); AddToJSon('stateOrProvinceCode','GA'); AddToJSon('postalCode','20783'); AddToJSon('countryCode','US'); AddToJSon('residential',FALSE); EndJSonBranch; //shipper //Destination AddJSonBranch('recipient'); AddJSonBranch('address'); CLEAR(ArrayText); ArrayText[1] := '7292 Dictum Ave'; ArrayText[2] := 'Unit 400'; AddArrayTxtProperty2Json('streetLines',ArrayText); EndJSonBranch; // address AddToJSon('city','San Antonio'); AddToJSon('stateOrProvinceCode','MI'); AddToJSon('postalCode','47096'); AddToJSon('countryCode','US'); AddToJSon('residential',FALSE); EndJSonBranch; //recipient CLEAR(ArrayText); ArrayText[1] := 'ACCOUNT'; AddArrayTxtProperty2Json('rateRequestType',ArrayText); //ERROR HAPPENS HERE AddToJSon('pickupType','USE_SCHEDULED_PICKUP'); EndJSonBranch; //requestedShipment
Regards0 -
Thank you, once again. I never would have thought to do that since I'm unfamiliar with all these assembly types. It worked like a charm and my payload appears to match what FedEx is after.
I was able to figure out the missing variable in the first reply, but I appreciate you updating here for future reference if anyone else has similar needs to those covered in this thread.
Can you recommend any resources that I might be able to use to learn more about working with the newtonsoft.dll? I'm sure I'll encounter more issues when I get to the point of parsing the response and I'd love to be more self-reliant.0 -
Hello @jeighsohn,
Sorry but I don't have any documentation or resource about the newtonsoft.dll, is more about working with JSON, using Google and trial and error.
Regards.1
Categories
- All Categories
- 73 General
- 73 Announcements
- 66.6K Microsoft Dynamics NAV
- 18.7K NAV Three Tier
- 38.4K NAV/Navision Classic Client
- 3.6K Navision Attain
- 2.4K Navision Financials
- 116 Navision DOS
- 851 Navision e-Commerce
- 1K NAV Tips & Tricks
- 772 NAV Dutch speaking only
- 617 NAV Courses, Exams & Certification
- 2K Microsoft Dynamics-Other
- 1.5K Dynamics AX
- 320 Dynamics CRM
- 111 Dynamics GP
- 10 Dynamics SL
- 1.5K Other
- 990 SQL General
- 383 SQL Performance
- 34 SQL Tips & Tricks
- 35 Design Patterns (General & Best Practices)
- 1 Architectural Patterns
- 10 Design Patterns
- 5 Implementation Patterns
- 53 3rd Party Products, Services & Events
- 1.6K General
- 1.1K General Chat
- 1.6K Website
- 83 Testing
- 1.2K Download section
- 23 How Tos section
- 252 Feedback
- 12 NAV TechDays 2013 Sessions
- 13 NAV TechDays 2012 Sessions