boris.nihil 0 Posted October 30, 2021 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
David Heffernan 2345 Posted October 31, 2021 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. 1 Share this post Link to post
boris.nihil 0 Posted November 1, 2021 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
Remy Lebeau 1394 Posted November 1, 2021 (edited) 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 November 1, 2021 by Remy Lebeau 1 Share this post Link to post
boris.nihil 0 Posted November 1, 2021 uh, it's very complicated.. Thank a lot on your effort Share this post Link to post
Alexander Elagin 143 Posted November 2, 2021 9 hours ago, boris.nihil said: uh, it's very complicated.. Have a look at Andreas Hausladen's JsonDataObjects (https://github.com/ahausladen/JsonDataObjects), I think it is easier to use than the stock TJSONObject. Share this post Link to post
boris.nihil 0 Posted November 2, 2021 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
Remy Lebeau 1394 Posted November 2, 2021 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
boris.nihil 0 Posted November 2, 2021 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
Remy Lebeau 1394 Posted November 2, 2021 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
boris.nihil 0 Posted November 2, 2021 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
Remy Lebeau 1394 Posted November 3, 2021 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
boris.nihil 0 Posted November 3, 2021 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
Remy Lebeau 1394 Posted November 4, 2021 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
boris.nihil 0 Posted November 6, 2021 you are right, thanks for you help and your time Share this post Link to post