Jump to content
boris.nihil

New to JSON

Recommended Posts

i can't figure out how to parse json answer from some invoice company

I use Delphi XE4, DBXJSON...

How to get Jsonpairs insde nested objects...

 

 

Example:

{

"messageAck": {
"messageID": "Middleware_002",
"messageAckID": "8f50dd66-a77f-45ec-9049-0faadb69ff43",
"messageType": 9002,
"ackStatus": "ACCEPTED",
"ackStatusCode": 10,
"ackStatusText": "Poruka zaprimljena!"
},

"b2GOutgoingInvoiceEnvelope": {
       "b2GOutgoingInvoiceProcessing": {
          "correctB2GOutgoingInvoice": {"supplierInvoiceID": "dokument1","invoiceID": 84001,"invoiceTimestamp": "03-03-2020"},
"incorrectB2GOutgoingInvoice": null}}

}
 

Share this post


Link to post

There are lots of articles and resources and examples on JSON parsing on the web. What have you found and what have you tried. Did you start by parsing a very simple bit of JSON first. You shouldn't try to learn something new by trying to solve your ultimate problem. Start simple and work up. 

  • Like 1

Share this post


Link to post

you are right, from small to big picture

i think this will do:

  JSonObject := TJSonObject.Create;
  JsonValue := JSonObject.ParseJSONValue(memoRequest.Lines.Text);

  JsonValue := (JsonValue as TJSONObject).Get('b2GOutgoingInvoiceEnvelope').JSONValue;
  JsonValue := (JsonValue as TJSONObject).Get('b2GOutgoingInvoiceProcessing').JSONValue;
  JsonValue := (JsonValue as TJSONObject).Get('correctB2GOutgoingInvoice').JSONValue;
  JsonValue := (JsonValue as TJSONObject).Get('invoiceID').JSONValue;
 

Share this post


Link to post
9 hours ago, boris.nihil said:

  JSonObject := TJSonObject.Create;

  JsonValue := JSonObject.ParseJSONValue(memoRequest.Lines.Text);

ParseJSONValue() is a 'class' function, so you don't need to instantiate an object to call it:

JsonValue := TJSonObject.ParseJSONValue(memoRequest.Lines.Text);

But more importantly:

9 hours ago, boris.nihil said:

JsonValue := JSonObject.ParseJSONValue(memoRequest.Lines.Text);

JsonValue := (JsonValue as TJSONObject).Get('b2GOutgoingInvoiceEnvelope').JSONValue;
...

You are responsible for Free'ing the TJSONValue that ParseJSONValue() returns, so your example is creating a memory leak when it re-assigns the JsonValue variable.

 

Try something more like this instead:

var
  JsonValue: TJSONValue;
  JsonObject: TJSONObject;
  invoice: Integer;

  function GetValue(JsonObject: TJSONObject; const PairName: string): TJSONValue;
  var
    JsonPair: TJSONPair;
  begin
    JsonPair := JsonObject.Get(PairName);
    if JsonPair = nil then
      raise Exception.Create('Pair "' + PairName + '" not found');
    Result := JsonPair.JsonValue;
  end;

  function GetObject(JsonObject: TJSONObject; const PairName: string): TJSONObject;
  begin
    Result := GetValue(JsonObject, PairName) as TJSONObject;
  end;
  
begin
  ...
  invoice := 0;
  JsonValue := TJSONObject.ParseJSONValue(memoRequest.Lines.Text);
  if JsonValue = nil then begin
    // error handling ...
  end else
  try
    try
      JsonObject := JsonValue as TJSONObject;
      JsonObject := GetObject(JsonObject, 'b2GOutgoingInvoiceEnvelope');
      JsonObject := GetObject(JsonObject, 'b2GOutgoingInvoiceProcessing');
      JsonObject := GetObject(JsonObject, 'correctB2GOutgoingInvoice');
      invoice := (GetValue(JsonObject, 'invoiceID') as TJSONNumber).AsInt;
    except
      // error handling ...
    end;
  finally
    JsonValue.Free;
  end;
  // use invoice as needed ...
end;

If you ever upgrade to XE6 or later, you can use the TJSONObject.FindValue() method instead:

type
  TJSONObjectAccess = class(TJSONObject)
  end;

var
  JsonValue, InvoiceJsonValue: TJSONValue;
  JsonObject: TJSONObject;
  invoice: Integer;
begin
  ...
  invoice := 0;
  JsonValue := TJSONObject.ParseJSONValue(memoRequest.Lines.Text);
  if JsonValue = nil then begin
    // error handling ...
  end else
  try
    try
      JsonObject := JsonValue as TJSONObject;
      InvoiceJsonValue := TJSONObjectAccess(JsonObject).FindValue('$.b2GOutgoingInvoiceEnvelope.b2GOutgoingInvoiceProcessing.correctB2GOutgoingInvoice.invoiceID');
      if InvoiceJsonValue = nil then
        raise Exception.Create('invoiceID not found');
      invoice := (InvoiceJsonValue as TJSONNumber).AsInt;
    except
      // error handling ...
    end;
  finally
    JsonValue.Free;
  end;
  // use invoice as needed ...
end;

 

Edited by Remy Lebeau
  • Like 1

Share this post


Link to post

it works fine,thanks

but now i have some array inside object

how to acces this documentStatus element ?

 

 

{
"messageAck": {
"messageID": "Middleware_002",
"messageAckID": "a98d281b-9608-4b25-be03-7be0403916dd",
"messageType": 9012,
"ackStatus": "ACCEPTED",
"ackStatusCode": 10,
"ackStatusText": "Message received!"
},
"b2GOutgoingInvoiceStatus": {
  "supplierID": "9934:86167814130",
  "additionalSupplierID": null,
"invoiceID": 83703,
"supplierInvoiceID": "IR-19-1766-1-1",
"invoiceTimestamp": "28-02-2020",


"documentStatus": [
{
"statusCode": "RECEIVED",
"statusText": "Received",
"statusTimestamp": "28-02-2020",
"note": null,
"partialAmount": null} ]

}
}
 

Share this post


Link to post

Using Delphi's stock JSON library, you already know how to access a named field of a TJSONObject.  So, you can simply retrieve the TJSONObject for "b2GOutgoingInvoiceStatus", then retrieve the TJSONValue for its "documentStatus" field, then type-cast that to TJSONArray, and then iterate through its Items[] property, casting each TJSONValue in the array to TJSONObject.

 

Using the JsonDataObjects library instead, you would have to do all of that using whatever its equivalent operations are.

Share this post


Link to post

something like this, seems to work:

 

          jasonArr := GetValue(JsonObject, 'documentStatus') as TJSONArray;
          for JsonValueArr  in jasonArr do
          begin
            for LItem in TJSONArray(JsonValueArr) do
            begin
              ShowMessage(TJSONPair(LItem).JsonString.Value + '/' + TJSONPair(LItem).JsonValue.Value);
            end;
          end;
 

Share this post


Link to post
4 hours ago, boris.nihil said:

something like this, seems to work:

Almost.  The documentStatus element is an array of objects, not an array of arrays, so you need to cast JsonValueArr to TJSONObject, not to TJSONArray:

jasonArr := GetValue(JsonObject, 'documentStatus') as TJSONArray;
for JsonValueArr  in jasonArr do
begin
  for LItem in TJSONObject(JsonValueArr) do
  begin
    ShowMessage(TJSONPair(LItem).JsonString.Value + '/' + TJSONPair(LItem).JsonValue.Value);
  end;
end;

 

Share this post


Link to post

when i write

for LItem in TJSONObject(JsonValueArr) do

it says:

[dcc32 Error] Unit1.pas(568): E2010 Incompatible types: 'TJSONValue' and 'TJSONPair'

Share this post


Link to post

JsonValueArr is pointing at a TJSONObject, not a TJSONArray, hence why you need to fix that typecast.

 

Enumerating a TJSONObject in a for..in loop will give you TJSONPair elements. What do you have LItem declared as, and why are you type-casting it to TJSONPair inside the loop?

 

JSON object- how to iterate through all properties without knowing their names?

Share this post


Link to post

this works ok..

 

var

  jasonArr:TJSONArray;
  par: TJSONPair;
  i: Integer;
  LItem:TJSONValue;
  JsonValueArr : TJSONValue;
  testValue: TJSONValue;
begin

          jasonArr := GetValue(JsonObject, 'documentStatus') as TJSONArray;
          for JsonValueArr  in jasonArr do
          begin
            for LItem in TJSONArray(JsonValueArr) do
            begin
              if TJSONPair(LItem).JsonString.Value = 'statusCode' then
                odgovorFinaStatus.b2gStatus := TJSONPair(LItem).JsonValue.Value else
              if TJSONPair(LItem).JsonString.Value = 'statusText' then
                odgovorFinaStatus.b2gStatusTekst := TJSONPair(LItem).JsonValue.Value else
              if TJSONPair(LItem).JsonString.Value = 'statusTimestamp' then
                odgovorFinaStatus.b2gStatusVrijeme := TJSONPair(LItem).JsonValue.Value else
              if TJSONPair(LItem).JsonString.Value = 'note' then
                odgovorFinaStatus.b2gStatusNapomena := TJSONPair(LItem).JsonValue.Value else
              if TJSONPair(LItem).JsonString.Value = 'partialAmount' then
              begin
                try
                  odgovorFinaStatus.b2gDjelIzn := StrToFloat(TJSONPair(LItem).JsonValue.Value);
                except
                  odgovorFinaStatus.b2gDjelIzn := 0
                end;
              end;
 

Share this post


Link to post
19 hours ago, boris.nihil said:

          jasonArr := GetValue(JsonObject, 'documentStatus') as TJSONArray;

          for JsonValueArr  in jasonArr do
          begin
            for LItem in TJSONArray(JsonValueArr) do // <-- THIS LINE IS WRONG!
            begin

The documentStatus field is an array of OBJECTS, not an array of ARRAYS.  Look at the JSON again more carefully, each element of the array is wrapped in curly braces (object), not square brackets (array).  As such, when iterating the array, the JsonValueArr variable will point at each element of the array, so you MUST type-cast it to TJSONObject, NOT to TJSONArray.  Otherwise your code will have undefined behavior due to an incorrect type-cast.  If you were to change the type-cast to use 'as' instead, you would get a runtime error.

19 hours ago, boris.nihil said:

              if TJSONPair(LItem).JsonString.Value = 'statusCode' then

You should not need to type-cast LItem as TJSONPair if you were to declare it as TJSONPair to begin with, rather than TJSONValue.  Enumerating a TJSONObject in a for..in loop yields TJSONPair elements.

 

Try this instead:

var
  jasonArr: TJSONArray;
  JsonValueArr : TJSONValue;
  LItem: TJSONPair;
  partialAmount: Double;
begin
  ...
  jasonArr := GetValue(JsonObject, 'documentStatus') as TJSONArray;
  for JsonValueArr in jasonArr do
  begin
    for LItem in TJSONObject(JsonValueArr){JsonValueArr as TJSONObject} do
    begin
      if LItem.JsonString.Value = 'statusCode' then
        odgovorFinaStatus.b2gStatus := LItem.JsonValue.Value
      else if LItem.JsonString.Value = 'statusText' then
        odgovorFinaStatus.b2gStatusTekst := LItem.JsonValue.Value
      else if LItem.JsonString.Value = 'statusTimestamp' then
        odgovorFinaStatus.b2gStatusVrijeme := LItem.JsonValue.Value
      else if LItem.JsonString.Value = 'note' then
        odgovorFinaStatus.b2gStatusNapomena := LItem.JsonValue.Value
      else if LItem.JsonString.Value = 'partialAmount' then
      begin
        if not TryStrToFloat(LItem.JsonValue.Value, partialAmount) then
          partialAmount := 0.0;
        odgovorFinaStatus.b2gDjelIzn := partialAmount;
      end;
  ...

 

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×