NAV 2009 R2 Classic Client post inform by API + JSON

Hi,

I am using NAV 2009 R2 classic client, would like to do an API post function to put PO Header and PO Line information to 3rd parties by using API + JSON. May I know how to do it? And how to get the response message from 3rd parties and showing success or error message to user / log into a table for user to check the transmission result.

Thanks!

Answers

  • SanderDkSanderDk Member Posts: 497
    Hi kiyachu,
    I do believe with NAV 2009 you can only do SOAP service
    For help, do not use PM, use forum instead, perhaps other people have the same question, or better answers.
  • kiyachukiyachu Member Posts: 7
    Thanks, SanderDk

    SOAP service means not API + JSON
    or
    I can carry out API + JSON via SOAP?

    Any details post for that?
    thanks a lot!
  • SanderDkSanderDk Member Posts: 497
    You cannot carry out json, SOAP is based on XML :smile:
    Any details about what?
    Setting up NAV service to run webservice?
    Communication with NAV webservice?
    For help, do not use PM, use forum instead, perhaps other people have the same question, or better answers.
  • ftorneroftornero Member Posts: 522
    Hello @kiyachu,

    In your NAV version if you publish an WS it is a SOAP one and the data interchanged are XML , but if you want to consume an external WS it could be SOAP/XML or REST/JSON.

    What's it your case ?

    Regards
  • kiyachukiyachu Member Posts: 7
    Thanks SanderDk & ftornero

    The 3rd parties software that I POST information to require REST/JSON.
    So what workaround I can choose with NAV version 2009 SP1?
    Any link I can follow to work the solution out, many thanks for your advise.
  • SanderDkSanderDk Member Posts: 497
    edited 2019-09-24
    I misunderstood your answer
    #delete
    For help, do not use PM, use forum instead, perhaps other people have the same question, or better answers.
  • ftorneroftornero Member Posts: 522
    Hello @kiyachu,

    If I don't miss anything I think that you need to call a REST API from NAV, so you can do it with somethig like this:

        CREATE(WinHttp);
        WinHttp.Open('POST',url,FALSE);
        WinHttp.SetRequestHeader('Content-Type', 'application/json');
        WinHttp.Send(JsonTxt);
        ResponseStream := WinHttp.ResponseStream;
        ResponseBuffer.READ(ResponseStream);
    

    Where the vars are:
    ResponseStream	InStream		
    ResponseBuffer	BigText
    url	Text		1024
    JsonTxt	Text		1024
    
    WinHttp	Automation	'Microsoft WinHTTP Services, version 5.1'.WinHttpRequest			
    

    If the JSON to send it's bigger than 1024 you need to use some workaround like an XML node text.

    Regards
  • kiyachukiyachu Member Posts: 7
    HI ftornero,

    Thanks for the code, may I have more e.g. where to state the API address, API key?
    For the Post content, as per your suggestion, I use XML node text to store and then converse it to JSON format before send out?
    May I have more code example for reference, many thanks!

  • ftorneroftornero Member Posts: 522
    Hello @kiyachu,

    The API address goes in the url variable.

    If you need to pass the API key it depends of what it's the REST API expecting, this would be an example for HubSpot, but you must check the API documentation to be sure:
    url := 'https://api.hubapi.com/contacts/v1/lists/all/contacts/all?&count=100&hapikey=' +hapikey;
    

    And regarding the JSON part you need to build the structure like this:
      NodeText := XMLDoc.createTextNode('');
      NodeText.appendData('{ "product": {');
      NodeText.appendData( '"sku":"1234567890",');
      NodeText.appendData('"extension_attributes": { "stock_Item": {');
      NodeText.appendData( '"qty": 60');
      NodeText.appendData('},');
      NodeText.appendData('},');
      NodeText.appendData('},');
      NodeText.appendData('}');
    

    Where the vars are:
    Name	DataType	Subtype	Length
    XMLDoc	Automation	'Microsoft XML, v3.0'.DOMDocument	
    NodeText	Automation	'Microsoft XML, v3.0'.IXMLDOMText	
    

    And later on pas the value to the REST API.
    WinHttp.Send(NodeText.nodeValue);
    

    Regards
  • kiyachukiyachu Member Posts: 7
    Hi ftornero,

    I got an error of
    "This Automation variable has not been instantiated.
    You can instantiate it by either creating or assigning it."

    Code :

    IF ISCLEAR(WinHttp) THEN
    CREATE(WinHttp);

    url := 'https://jsonplaceholder.typicode.com/users';
    WinHttp.Open('POST',url,FALSE);
    WinHttp.SetRequestHeader('Content-Type', 'application/json');
    WinHttp.Send(JsonTxt);
    ResponseStream := WinHttp.ResponseStream;
    ResponseBuffer.READ(ResponseStream);


    NodeText := XMLDoc.createTextNode('');
    NodeText.appendData('{ "id": "111",');
    NodeText.appendData( '"name":"A",');
    NodeText.appendData('"username": "X"');
    NodeText.appendData('}');

    WinHttp.Send(NodeText.nodeValue);

    Variable
    Name DataType Subtype Length
    WinHttp Automation 'Microsoft WinHTTP Services, version 5.1'.WinHttpRequest
    NodeText Automation 'Microsoft XML, v3.0'.IXMLDOMText
    XMLDoc Automation 'Microsoft XML, v3.0'.DOMDocument

    I use report to run it.
    Version : NAV 2009 SP21 - Classic client

    Pls comment, thanks!
  • ftorneroftornero Member Posts: 522
    Hello @kiyachu ,

    You need to creaste the XMLDoc before use it.
    CREATE(XMLDoc);
    NodeText := XMLDoc.createTextNode('');
    

    Regards
  • kiyachukiyachu Member Posts: 7
    Hi ftornero,

    No error after adding create XMLDoc. Thanks!

    Now, I would like to know where and how to get the response from server after I post information to server?
    I would like to prompt a message to user for showing success or not and the ID #, how can I get those from response and show it to user as message box in NAV and/or write to a table for user to check whether the post action success or not for that ID#

    Like this I got when I use postman for test of json content

    8gxnqhfzsczk.jpg

  • ftorneroftornero Member Posts: 522
    Hello @kiyachu ,

    After sending the request you can get the response like this:
          ResponseStream := WinHttp.ResponseStream;
    

    Where ResponseStream is a InStream variable.

    Regards.
  • kiyachukiyachu Member Posts: 7
    Hi ftornero,

    How can we read ResponseStream and display it as message or store into a table?
    I add "MESSAGE(FORMAT(ResponseStream))" at the end, but the message box show empty?
    ========================================
    IF ISCLEAR(WinHttp) THEN
    CREATE(WinHttp);

    url := 'https://jsonplaceholder.typicode.com/users';
    WinHttp.Open('POST',url,FALSE);
    WinHttp.SetRequestHeader('Content-Type', 'application/json');
    WinHttp.Send(JsonTxt);
    ResponseStream := WinHttp.ResponseStream;
    ResponseBuffer.READ(ResponseStream);

    CREATE(XMLDoc);
    NodeText := XMLDoc.createTextNode('');
    NodeText.appendData('{ "id": "111",');
    NodeText.appendData( '"name":"A",');
    NodeText.appendData('"username": "X"');
    NodeText.appendData('}');


    WinHttp.Send(NodeText.nodeValue);
    MESSAGE(FORMAT(ResponseStream)); //ADD FOR SHOWING RESPONSE
  • ftorneroftornero Member Posts: 522
    Hello @kiyachu ,

    You could get this InStream variable and fill a BigText with it:
          ResponseBuffer.READ(ResponseStream);
    

    Where ResponseBuffer is the BigText var.

    Later on you can deal with this var to show your message.

    You can also write to a file and read the file to do the same.

    Regards.
  • ChuChuChuChu Member Posts: 5
    Hi ftornero,

    I have followed this post and get it works on sending JSON API under NAV2009SP1 version.

    Now my question is, how can I save the outstream as a text file, so that I can keep a copy on what I have send out (The POST content).

    Many thanks! It is my last step for this customization!!!!!

  • ftorneroftornero Member Posts: 522
    Hello @ChuChu ,

    You can do something like this:
        i := 0;
        Fich.CREATE(Path+'data.json');
        WHILE i <= NodeText.length DO BEGIN
          Fich.WRITE(NodeText.substringData(i, 1));
          i := i + 1;
        END;
        Fich.CLOSE;
    

    Where the variables are:

    i is an Integer

    Fich is a FILE

    Path is a Text with the Path where you want to create the output file.

    Regards
  • ChuChuChuChu Member Posts: 5
    Hi ftornero,

    Yes, it works fine for your coding.
    Thank you very much!!!!

    Regards
  • VishalAgVishalAg Member Posts: 5
    Hi @ftornero ,

    I wanted to know if there is a way to send few rows and columns of a table based on filter through it instead of single value. If so, please help me with some example.

    Thank You
  • ftorneroftornero Member Posts: 522
    Hello @VishalAg

    Yes, you can send the information you want but it depends on what the web service you are connected is expecting.

    Regards.
  • azharsaeedkhanazharsaeedkhan Member Posts: 34
    Hello @ftornero
    I am stuck in a situation where i have a long json response (i.e. > 1024 characters) from the service.
    Can you please guide me how to handle such a long text, plus i have to manipulate the data using my logic which means that this data should be available in some variable, so i can use it further.
  • ftorneroftornero Member Posts: 522
    Hello @azharsaeedkhan,

    You can save the response how is displayed in the previous posts to a file and then read file with something like this, o save to a BigText variable and skip the file altogether.
    ParseJson(Fichero : Text[1024];VAR Buffer : Record "Buffer JSON")
    CLEAR(Buffer);
    Buffer.DELETEALL;
    // Put the file content to a BigText
    Fich.TEXTMODE(FALSE);
    Fich.WRITEMODE(FALSE);
    Fich.OPEN(Fichero);
    qLen := Fich.LEN;
    Linea := '';
    WHILE (Fich.POS < qLen) DO BEGIN
       Fich.READ(qChar);
       BgText.ADDTEXT(FORMAT(qChar));
    END;
    
    // Get the BigText char to char
    esClave := TRUE;
    key := '';
    Value := '';
    enComillas := FALSE;
    nivel := 0;
    p := 1;
    l := BgText.LENGTH;
    
    WHILE p < l DO BEGIN
      BgText.GETSUBTEXT(Aux, p, 1);
      CASE Aux OF
        ':': BEGIN  // Change from key to value
               IF enComillas THEN BEGIN
                 IF esClave THEN
                   key := key + Aux
                 ELSE
                   Value := Value + Aux;
               END ELSE
                 esClave := FALSE
             END;
        ',': BEGIN  // New key - value pair
               IF enComillas THEN BEGIN
                 IF esClave THEN
                   key := key + Aux
                 ELSE
                   Value := Value + Aux;
               END ELSE BEGIN
                 esClave := TRUE;
                 IF key <> '' THEN
                   InsLogTemp(Buffer, key, Value, nivel);
               END;
             END;
        '"': enComillas := NOT enComillas;
        '{': BEGIN
               nivel := nivel + 1;
               key := '';
               esClave := TRUE;
             END;
        '}': BEGIN
               IF key <> '' THEN
                 InsLogTemp(Buffer, key, Value, nivel);
               nivel := nivel - 1;
             END;
        '[': BEGIN
               nivel := nivel + 1;
               //esClave := TRUE;
             END;
        ']': BEGIN
               nivel := nivel - 1;
             END;
    //    '\': ;
        ELSE BEGIN
          // Add a kye or a value
          IF esClave THEN
            key := key + Aux
          ELSE
            Value := Value + Aux;
        END;
      END;
      p := p + 1;
    END;
    IF key <> '' THEN
      InsLogTemp(Buffer, key, Value, nivel);
    
    

    Where table "Buffer JSON" is a temporary table with this structure:

    pzqevgpqzoud.png


    And the function InsLogTemp is this one:
    InsLogTemp(VAR Buffer : Record "Buffer JSON";VAR Clave : Text[1024];VAR Valor : Text[1024];nivel : Integer)
    IF Buffer.FINDLAST THEN
      Buffer.NMov := Buffer.NMov + 1
    ELSE
      Buffer.NMov := 1;
    
    Buffer.Nivel := nivel;
    Buffer.Clave := COPYSTR(Clave, 1, MAXSTRLEN(Buffer.Clave));
    Buffer.Valor := COPYSTR(Valor, 1, MAXSTRLEN(Buffer.Valor));
    Buffer.INSERT;
    Clave := '';
    Valor := '';
    
    

    After that you can read the "Buffer JSON" table and deal with the values in there.

    Regards
  • ftorneroftornero Member Posts: 522
    The local variables in ParseJson:
    Name	DataType	Subtype	Length
    Fich	File		
    key	Text		1024
    Value	Text		1024
    p	Integer		
    l	Integer		
    qLen	Integer		
    Linea	Text		1024
    BgText	BigText		
    qChar	Char		
    Aux	Text		1
    esClave	Boolean		
    enComillas	Boolean		
    nivel	Integer		
    
  • azharsaeedkhanazharsaeedkhan Member Posts: 34
    ftornero wrote: »
    Hello @azharsaeedkhan,

    You can save the response how is displayed in the previous posts to a file and then read file with something like this, o save to a BigText variable and skip the file altogether.
    ParseJson(Fichero : Text[1024];VAR Buffer : Record "Buffer JSON")
    CLEAR(Buffer);
    Buffer.DELETEALL;
    // Put the file content to a BigText
    Fich.TEXTMODE(FALSE);
    Fich.WRITEMODE(FALSE);
    Fich.OPEN(Fichero);
    qLen := Fich.LEN;
    Linea := '';
    WHILE (Fich.POS < qLen) DO BEGIN
       Fich.READ(qChar);
       BgText.ADDTEXT(FORMAT(qChar));
    END;
    
    // Get the BigText char to char
    esClave := TRUE;
    key := '';
    Value := '';
    enComillas := FALSE;
    nivel := 0;
    p := 1;
    l := BgText.LENGTH;
    
    WHILE p < l DO BEGIN
      BgText.GETSUBTEXT(Aux, p, 1);
      CASE Aux OF
        ':': BEGIN  // Change from key to value
               IF enComillas THEN BEGIN
                 IF esClave THEN
                   key := key + Aux
                 ELSE
                   Value := Value + Aux;
               END ELSE
                 esClave := FALSE
             END;
        ',': BEGIN  // New key - value pair
               IF enComillas THEN BEGIN
                 IF esClave THEN
                   key := key + Aux
                 ELSE
                   Value := Value + Aux;
               END ELSE BEGIN
                 esClave := TRUE;
                 IF key <> '' THEN
                   InsLogTemp(Buffer, key, Value, nivel);
               END;
             END;
        '"': enComillas := NOT enComillas;
        '{': BEGIN
               nivel := nivel + 1;
               key := '';
               esClave := TRUE;
             END;
        '}': BEGIN
               IF key <> '' THEN
                 InsLogTemp(Buffer, key, Value, nivel);
               nivel := nivel - 1;
             END;
        '[': BEGIN
               nivel := nivel + 1;
               //esClave := TRUE;
             END;
        ']': BEGIN
               nivel := nivel - 1;
             END;
    //    '\': ;
        ELSE BEGIN
          // Add a kye or a value
          IF esClave THEN
            key := key + Aux
          ELSE
            Value := Value + Aux;
        END;
      END;
      p := p + 1;
    END;
    IF key <> '' THEN
      InsLogTemp(Buffer, key, Value, nivel);
    
    

    Where table "Buffer JSON" is a temporary table with this structure:

    pzqevgpqzoud.png


    And the function InsLogTemp is this one:
    InsLogTemp(VAR Buffer : Record "Buffer JSON";VAR Clave : Text[1024];VAR Valor : Text[1024];nivel : Integer)
    IF Buffer.FINDLAST THEN
      Buffer.NMov := Buffer.NMov + 1
    ELSE
      Buffer.NMov := 1;
    
    Buffer.Nivel := nivel;
    Buffer.Clave := COPYSTR(Clave, 1, MAXSTRLEN(Buffer.Clave));
    Buffer.Valor := COPYSTR(Valor, 1, MAXSTRLEN(Buffer.Valor));
    Buffer.INSERT;
    Clave := '';
    Valor := '';
    
    

    After that you can read the "Buffer JSON" table and deal with the values in there.

    Regards

    Thank you for your detailed answer (Y)
Sign In or Register to comment.