Related
I have to write a function for this app that has a grid where you can input value in rows, delete rows, update them etc.
When you do such operation, a JSON file is sent as payload and I can handle it with a procedure, but I am having problems with situations where a user wants to edit multiple rows, it updates just one of them.
This is an example of JSON file, where the description key is the one that is changed from null to the one with values (v1, v2, v3)
If value in grid is changed, it includes key "Changed" with value 1, in case of delete "Deleted" and so on.
{
"Changes": [
{
"id": "AR46",
"Changed": 1,
"SESSION_ID": "963",
"NAME": "IMAGE_LOGO",
"VALUE": "",
"DESCRIPTION": "v1",
"TYPE": "IMAGE",
"PARAM_GROUP": "J",
"BLOB_VALUE": "oracle.sql.BLOB#2ba32",
"EDIT": "Edit",
"DOWNLOAD": "Download",
"CLOB_VALUE": "oracle.sql.CLOB#7fd86843",
"XML_VALUE": "",
"CREATE_DATE": "11.03.2022 13:04:26",
"_DefaultSort": ""
},
{
"id": "AR47",
"Changed": 1,
"SESSION_ID": "963",
"NAME": "IMAGE_HPB_MEMO_FOOTER",
"VALUE": "",
"DESCRIPTION": "v2",
"TYPE": "IMAGE",
"PARAM_GROUP": "JASPER",
"BLOB_VALUE": "oracle.sql.BLOB#7621f9df",
"EDIT": "Edit",
"DOWNLOAD": "Download",
"CLOB_VALUE": "oracle.sql.CLOB#43e24152",
"XML_VALUE": "",
"CREATE_DATE": "11.03.2022 13:04:35",
"_DefaultSort": ""
},
{
"id": "AR48",
"Changed": 1,
"SESSION_ID": "963",
"NAME": "IMAGE_HPB_MEMO_INVCRED",
"VALUE": "",
"DESCRIPTION": "v3",
"TYPE": "IMAGE",
"PARAM_GROUP": "JASPER",
"BLOB_VALUE": "oracle.sql.BLOB#762074f6",
"EDIT": "Edit",
"DOWNLOAD": "Download",
"CLOB_VALUE": "oracle.sql.CLOB#4a068001",
"XML_VALUE": "",
"CREATE_DATE": "11.03.2022 13:04:46",
"_DefaultSort": ""
}
]
}
And this is the function that I wrote that works for just one edit/update. There aren't any errors if you try to update multiple rows, but still, just one(the first) one, is changed.
create or replace function changesResources (p_data varchar2)
return varchar2
IS
l_nullEx exception;
PRAGMA EXCEPTION_INIT(l_nullEx, -1400);
p_rez varchar2(100);
p_session_id number;
p_name varchar2(100);
p_value VARCHAR2(500);
p_description VARCHAR2(1000);
p_type VARCHAR2(100);
p_param_group VARCHAR2(100);
p_blob_value VARCHAR2(1000);
p_clob_value VARCHAR2(1000);
p_xml_value VARCHAR2(1000);
p_create_date varchar2(50);
l_json_obj JSON_OBJECT_T;
l_json_arr JSON_ARRAY_T;
Begin
l_json_obj := JSON_OBJECT_T.PARSE(p_data);
l_json_arr := l_json_obj.get_array('Changes');
FOR i IN 0..l_json_arr.get_size()-1 LOOP
p_session_id := JSON_VALUE(l_json_arr.get(i).to_string(), '$.SESSION_ID');
p_name := JSON_VALUE(l_json_arr.get(i).to_string(), '$.NAME');
p_value := JSON_VALUE(l_json_arr.get(i).to_string(), '$.VALUE');
p_description := JSON_VALUE(l_json_arr.get(i).to_string(), '$.DESCRIPTION');
p_type := JSON_VALUE(l_json_arr.get(i).to_string(), '$.TYPE');
p_param_group := JSON_VALUE(l_json_arr.get(i).to_string(), '$.PARAM_GROUP');
p_blob_value := JSON_VALUE(l_json_arr.get(i).to_string(), '$.BLOB_VALUE');
p_clob_value := JSON_VALUE(l_json_arr.get(i).to_string(), '$.CLOB_VALUE');
p_xml_value := JSON_VALUE(l_json_arr.get(i).to_string(), '$.XML_VALUE');
p_create_date := JSON_VALUE(l_json_arr.get(i).to_string(), '$.CREATE_DATE');
IF JSON_VALUE(l_json_arr.get(i).to_string(), '$.Changed') = 1
THEN
UPDATE BF_RESOURCES_CONF
SET description = p_description,
value=p_value,
type = p_type,
param_group = p_param_group,
blob_value = utl_raw.cast_to_raw(p_blob_value),
clob_value = TO_CLOB(p_clob_value),
xml_value=p_xml_value,
create_date = TO_DATE(p_create_date,'DD.MM.YYYY HH24:MI:SS')
where session_id = p_session_id
and name = p_name;
p_rez := '1|success!';
return p_rez;
ELSIF JSON_VALUE(l_json_arr.get(i).to_string(), '$.Deleted') = 1
THEN
DELETE FROM BF_RESOURCES_CONF
WHERE session_id = p_session_id
and name = p_name;
p_rez := '1|success!';
return p_rez;
ELSE
INSERT INTO BF_RESOURCES_CONF (session_id,name, value,description, type,param_group,blob_value,clob_value,xml_value,create_date) VALUES (p_session_id, p_name, p_value, p_description, p_type, p_param_group, utl_raw.cast_to_raw(p_blob_value),TO_CLOB(p_clob_value),p_xml_value,TO_DATE(p_create_date,'DD.MM.YYYY HH24:MI:SS'));
p_rez := '1|success!';
return p_rez;
END IF;
END LOOP;
EXCEPTION
WHEN l_nullEx THEN
p_rez := '-1|Columns SESSION_ID, NAME I CREATE_DATE have to contain values!';
RETURN p_rez;
--WHEN OTHERS THEN
-- p_rez := '-1|Error!';
-- RETURN p_rez;
END changesResources ;
On 12.1 and above JSON_TABLE is available. Here is an example on the emp sample table similar to yours:
CREATE OR REPLACE function update_emp (p_data VARCHAR2)
RETURN VARCHAR2
IS
l_result VARCHAR2(1000);
BEGIN
FOR r IN (
with json_doc AS
(SELECT p_data AS json_data FROM dual
)
SELECT
empno,
changed,
deleted,
salary
FROM
json_doc t,
JSON_TABLE(json_data, '$.Changes[*]'
COLUMNS (
empno NUMBER PATH '$.empno',
changed NUMBER PATH '$.Changed',
deleted NUMBER PATH '$.Deleted',
salary NUMBER PATH '$.Salary'
))
) LOOP
IF r.changed = 1 THEN
UPDATE emp SET sal = r.salary WHERE empno = r.empno;
l_result := l_result || ', updated: '||r.empno;
ELSIF r.deleted = 1 THEN
DELETE FROM emp WHERE empno = r.empno;
l_result := l_result || ', deleted: '||r.empno;
END IF;
END LOOP;
RETURN LTRIM(l_result, ', ');
END update_emp;
/
set serveroutput on size 999999
clear screen
declare
l_data varchar2(1000);
l_return varchar2(1000);
begin
l_data := '{
"Changes": [
{
"empno": 7698,
"Changed": 1,
"Salary": 4000,
},
{
"empno": 7788,
"Changed": 1,
"Salary": 5000,
},
{
"empno": 7876,
"Deleted": 1
}
]
}';
l_return := update_emp(p_data => l_data);
dbms_output.put_line('l_return = ' || l_return);
end;
/
l_return = updated: 7698, updated: 7788, deleted: 7876
PL/SQL procedure successfully completed.
I am trying to get nested field's values from json which is returned by function apex_web_service.make_rest_request.
DECLARE
v_address_json clob;
v_address_response clob;
l_object_address json_object_t;
BEGIN
SELECT
json_object ('Addresses' value json_object(
'AddName' value ('HQ') ,
'AddLine1' value tab1.ADDRESS_LINE1 ,
.
.
.
.
into
v_address_json
FROM
tab1 t1,
tab2 t2
WHERE
.....
.....;
v_address_response := apex_web_service.make_rest_request
(
p_url => 'https://...../addresses',
p_http_method => 'POST',
p_body => v_address_json
);
DBMS_OUTPUT.PUT_LINE('v_address_response : '||v_address_response);
At this point I am getting below in v_address_response.
{
"Metadata" : {
"application" : "",
"applicationRefId" : ""
},
"APIResponse" : {
"Status" : "SUCCESS",
"Error" : {
"Code" : "",
"Message" : "",
"Detail" : ""
}
},
"ven" : {
"Id" : 12345,
"Addresses" : [ {
"vAddId" : 1122334455,
"vAddName" : "HQ",
"AddLine1" : "1/A2, ABC, XYZ ROAD, IN",
"City" : "JKL",
"State" : "AB",
"PCode" : "102030",
"Country" : "IN",
"TaxReg" : [ {
"RegId" : 998877,
"EffectiveFrom" : "2029-11-13"
} ],
"TaxRep" : [ {
"TaxRepId" : 665544,
"EffectiveFrom" : "2022-01-01"
} ]
} ]
}
}
further I am trying to get field's value as below.
l_object_address := json_object_t.parse(v_address_response);
if l_object_address.get_Object('APIResponse').get_string('Status') = 'SUCCESS'
then
DBMS_OUTPUT.PUT_LINE(' New Address ID : '||l_object_address.get_Object('Addresses').get_string('vAddId')); --output xx
else
DBMS_OUTPUT.PUT_LINE('Error in creating address');
end if;
exception when others then
null;
end;
in the above section I am able to get value for APIResponse--> Status i.e 'SUCCESS' but unable to get value of vAddId, RegId and other nested fields. At comment, output xx, nothing is getting printed.
If you're just after reading values out of the JSON (and not intend to traverse the "object tree" up and down") I would use the JSON_TABLE SQL Function, as follows:
BEGIN
for i in (
select status,
vAddId,
vAddName,
RegId,
EffectiveFrom
-- , other columns go here
from json_table(
v_address_response,
'$'
columns(
status varchar2(255) path '$.APIResponse.Status',
-- other columns on this level go here
ven_id number path '$.ven.Id',
nested path '$.ven.Addresses' columns(
vAddId number path '$.vAddId',
vAddName varchar2(255) path '$.vAddName',
-- other columns on this level go here
nested path '$.TaxReg' columns(
RegId number path '$.RegId',
EffectiveFrom varchar2(255) path '$.EffectiveFrom'
)
)
)
)
)
loop
-- process the data here
if i.status = 'SUCCESS' then
DBMS_OUTPUT.PUT_LINE(' New Address ID : '|| i.vAddId);
else
DBMS_OUTPUT.PUT_LINE('Error in creating address');
end if;
end loop;
end;
/
fiddle
You need to descend into the ven object first and then Addresses is an array of objects, and not an object, so you need to find the zero-indexed element of the array and then get the attribute of the object. To do that, you can use:
DECLARE
v_address_response CLOB := '{
"Metadata" : {
"application" : "",
"applicationRefId" : ""
},
"APIResponse" : {
"Status" : "SUCCESS",
"Error" : {
"Code" : "",
"Message" : "",
"Detail" : ""
}
},
"ven" : {
"Id" : 12345,
"Addresses" : [ {
"vAddId" : 1122334455,
"vAddName" : "HQ",
"AddLine1" : "1/A2, ABC, XYZ ROAD, IN",
"City" : "JKL",
"State" : "AB",
"PCode" : "102030",
"Country" : "IN",
"TaxReg" : [ {
"RegId" : 998877,
"EffectiveFrom" : "2029-11-13"
} ],
"TaxRep" : [ {
"TaxRepId" : 665544,
"EffectiveFrom" : "2022-01-01"
} ]
} ]
}
}';
l_object JSON_OBJECT_T;
l_address JSON_OBJECT_T;
BEGIN
l_object := json_object_t.parse(v_address_response);
if l_object.get_Object('APIResponse').get_string('Status') = 'SUCCESS' then
l_address := TREAT( l_object.get_Object('ven').get_Array('Addresses').get(0) AS JSON_OBJECT_T );
DBMS_OUTPUT.PUT_LINE(' New Address ID : '||l_address.get_Number('vAddId')); --output xx
else
DBMS_OUTPUT.PUT_LINE('Error in creating address');
end if;
end;
/
Which outputs:
New Address ID : 1122334455
fiddle
I have a JSON like this:
{
"Content": [{
"Identifier": "AABBCC",
"Description": "test terfdfg",
"GenericProductIdentifier": "AABBCC",
"ProductFamilyDescription": "sampling",
"LifeCycleStatus": "ACTIVE",
"Price": {
"Value": 1.00,
"Quantity": 1000
},
"LeadTimeWeeks": "16",
"FullBoxQty": 200,
}],
"TotalElements": 1,
"TotalPages": 1,
"NumberOfElements": 1,
"First": true,
"Size": 1,
"Number": 0
}
In Delphi 10.4, I'm trying to parse it, but I can't access the values contained in 'Price'.
I wrote code like this:
var
vContent: TJSONArray;
vJson: TJSONObject;
vContentRow: TJSONObject;
i,j : Integer;
begin
Memo2.Lines.Clear;
if Memo1.Text = '' then
exit;
vJson := TJSONObject(TJSONObject.ParseJSONValue(TEncoding.ASCII.GetBytes(Memo1.Text),0));
try
vContent := TJSONArray(vJson.Get('Content').JsonValue);
for i := 0 to Pred(vContent.Count) do
begin
vContentRow := TJSONObject(vContent.Items[i]);
for j := 0 to Pred(vContentRow.Count) do
begin
Memo2.Lines.Add(' '+ vContentRow.Get(j).JsonString.Value+' : '+ vContentRow.Get(j).JsonValue.Value);
end;
end;
Memo2.Lines.Add(vContent.Value);
finally
end;
end;
What is the correct way to read the values contained in 'Price'?
Here is a sample code to parse your JSON:
uses
System.IOUtils, System.JSON, System.Generics.Collections;
procedure TForm1.Button1Click(Sender: TObject);
procedure GetPrices(const S: string);
var
V: TJsonValue;
O, E, P: TJsonObject;
A: TJsonArray;
begin
V := TJSONObject.ParseJSONValue(S);
if not Assigned(V) then
raise Exception.Create('Invalid JSON');
try
O := V as TJSONObject;
A := O.GetValue<TJsonArray>('Content');
for var I := 0 to A.Count - 1 do
begin
E := A.Items[I] as TJsonObject; // Element
P := E.GetValue<TJsonObject>('Price');
ShowMessage('Value: ' + P.GetValue<string>('Value') + ' ' + 'Quantity: ' + P.GetValue<string>('Quantity'));
end;
finally
V.Free;
end;
end;
var
S: string;
begin
S := TFile.ReadAllText('d:\json.txt'); // Retrieve it using some webservice
GetPrices(S);
end;
Note, your JSON is invalid, the corect definition is:
{
"Content": [{
"Identifier": "AABBCC",
"Description": "test terfdfg",
"GenericProductIdentifier": "AABBCC",
"ProductFamilyDescription": "sampling",
"LifeCycleStatus": "ACTIVE",
"Price": {
"Value": 1.00,
"Quantity": 1000
},
"LeadTimeWeeks": "16",
"FullBoxQty": 200
}],
"TotalElements": 1,
"TotalPages": 1,
"NumberOfElements": 1,
"First": true,
"Size": 1,
"Number": 0
}
You can use the JSON library of Delphi.
The JSON library has the JsonToObject class function that can convert directly the string to an Object (Object structure)
See this:
https://docwiki.embarcadero.com/Libraries/Sydney/en/REST.Json.TJson.JsonToObject
You can create the classes structure manually o using the web: https://jsontodelphi.com/
The classes structure for your JSON created is this:
type
TPrice = class;
TPrice = class
private
FQuantity: Integer;
FValue: Double;
published
property Quantity: Integer read FQuantity write FQuantity;
property Value: Double read FValue write FValue;
end;
TContent = class
private
FDescription: string;
FFullBoxQty: Integer;
FGenericProductIdentifier: string;
FIdentifier: string;
FLeadTimeWeeks: string;
FLifeCycleStatus: string;
FPrice: TPrice;
FProductFamilyDescription: string;
published
property Description: string read FDescription write FDescription;
property FullBoxQty: Integer read FFullBoxQty write FFullBoxQty;
property GenericProductIdentifier: string read FGenericProductIdentifier write FGenericProductIdentifier;
property Identifier: string read FIdentifier write FIdentifier;
property LeadTimeWeeks: string read FLeadTimeWeeks write FLeadTimeWeeks;
property LifeCycleStatus: string read FLifeCycleStatus write FLifeCycleStatus;
property Price: TPrice read FPrice;
property ProductFamilyDescription: string read FProductFamilyDescription write FProductFamilyDescription;
public
constructor Create;
destructor Destroy; override;
end;
TRoot = class(TJsonDTO)
private
[JSONName('Content'), JSONMarshalled(False)]
FContentArray: TArray<TContent>;
[GenericListReflect]
FContent: TObjectList<TContent>;
FFirst: Boolean;
FNumber: Integer;
FNumberOfElements: Integer;
FSize: Integer;
FTotalElements: Integer;
FTotalPages: Integer;
function GetContent: TObjectList<TContent>;
protected
function GetAsJson: string; override;
published
property Content: TObjectList<TContent> read GetContent;
property First: Boolean read FFirst write FFirst;
property Number: Integer read FNumber write FNumber;
property NumberOfElements: Integer read FNumberOfElements write FNumberOfElements;
property Size: Integer read FSize write FSize;
property TotalElements: Integer read FTotalElements write FTotalElements;
property TotalPages: Integer read FTotalPages write FTotalPages;
public
destructor Destroy; override;
end;
Now, the code for parse elements is more simple. You only need a code like this to access different properties of your structure:
var
Root: TRoot;
begin
root := TJSON.JsonToObject<TRoot>(Memo1.Lines.Text);
lblid.Caption := 'TotalElements: ' + Root.TotalElements.ToString;
lblvalue.Caption := 'TotalPages: ' + Root.TotalPages.ToString;
lblcount.Caption := 'Identifier: ' + Root.Content[0].Identifier;
lblfirstonclick.Caption := 'Description: ' + Root.Content[0].Description;
lbllastonclick.Caption := 'Price/Quantity:' + Root.Content[0].Price.Quantity.ToString;
//...
Try this, i make some helper for TFDMemtable. Simple to uses, no need parsing everytime you have other JSON.
const
JSONString =
'{
"Content": [{
"Identifier": "AABBCC",
"Description": "test terfdfg",
"GenericProductIdentifier": "AABBCC",
"ProductFamilyDescription": "sampling",
"LifeCycleStatus": "ACTIVE",
"Price": {
"Value": 1.00,
"Quantity": 1000
},
"LeadTimeWeeks": "16",
"FullBoxQty": 200,
}],
"TotalElements": 1,
"TotalPages": 1,
"NumberOfElements": 1,
"First": true,
"Size": 1,
"Number": 0
}';
begin
if not Memtable.FillDataFromString(JSONString) then begin
ShowMessages(Memtable.FieldByName('messages').AsString);
end else begin
Memtable.FillDataFromString(Memtable.FieldByName('Content').AsString);
ShowMessages(Memtable.FieldByName('Price').AsString);
end;
end;
====
unit BFA.Helper.MemTable;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Memo.Types,
System.Rtti, FMX.Grid.Style, FMX.Grid, FMX.ScrollBox, FMX.Memo, FMX.Edit,
FMX.Controls.Presentation, FMX.StdCtrls, FireDAC.Stan.Intf,
FireDAC.Stan.Option, FireDAC.Stan.Param, FireDAC.Stan.Error, FireDAC.DatS,
FireDAC.Phys.Intf, FireDAC.DApt.Intf, System.Net.URLClient,
System.Net.HttpClient, System.Net.HttpClientComponent, Data.DB,
FireDAC.Comp.DataSet, FireDAC.Comp.Client, System.JSON, System.Net.Mime;
type
TFDMemTableHelper = class helper for TFDMemTable
procedure FillError(FMessage, FError : String);
function FillDataFromString(FJSON : String) : Boolean; //ctrl + shift + C
end;
implementation
{ TFDMemTableHelper }
function TFDMemTableHelper.FillDataFromString(FJSON: String): Boolean; //bug memoryleak fix at checking is Object / array soon
const
FArr = 0;
FObj = 1;
FEls = 2;
function isCheck(FString : String) : Integer; begin
Result := FEls;
var FCheck := TJSONObject.ParseJSONValue(FJSON);
if FCheck is TJSONObject then
Result := FObj
else if FCheck is TJSONArray then
Result := FArr;
FCheck.DisposeOf;
end;
var
JObjectData : TJSONObject;
JArrayJSON : TJSONArray;
JSONCheck : TJSONValue;
begin
var FResult := isCheck(FJSON);
try
Self.Active := False;
Self.Close;
Self.FieldDefs.Clear;
if FResult = FObj then begin
JObjectData := TJSONObject.ParseJSONValue(FJSON) as TJSONObject;
end else if FResult = FArr then begin
JArrayJSON := TJSONObject.ParseJSONValue(FJSON) as TJSONArray;
JObjectData := TJSONObject(JArrayJSON.Get(0));
end else begin
Self.FillError('FAILED PARSING JSON', 'THIS IS NOT JSON');
Result := False;
Exit;
end;
for var i := 0 to JObjectData.Size - 1 do begin
Self.FieldDefs.Add(
StringReplace(JObjectData.Get(i).JsonString.ToString, '"', '', [rfReplaceAll, rfIgnoreCase]),
ftString,
100000,
False
);
end;
Self.CreateDataSet;
Self.Active := True;
Self.Open;
try
if FResult = FArr then begin
for var i := 0 to JArrayJSON.Size - 1 do begin
JObjectData := TJSONObject(JArrayJSON.Get(i));
Self.Append;
for var ii := 0 to JObjectData.Size - 1 do begin
JSONCheck := TJSONObject.ParseJSONValue(JObjectData.GetValue(Self.FieldDefs[ii].Name).ToJSON);
if JSONCheck is TJSONObject then
Self.Fields[ii].AsString := JObjectData.GetValue(Self.FieldDefs[ii].Name).ToJSON
else if JSONCheck is TJSONArray then
Self.Fields[ii].AsString := JObjectData.GetValue(Self.FieldDefs[ii].Name).ToJSON
else
Self.Fields[ii].AsString := JObjectData.Values[Self.FieldDefs[ii].Name].Value;
JSONCheck.DisposeOf;
end;
Self.Post;
end;
end else begin
Self.Append;
for var ii := 0 to JObjectData.Size - 1 do begin
JSONCheck := TJSONObject.ParseJSONValue(JObjectData.GetValue(Self.FieldDefs[ii].Name).ToJSON);
if JSONCheck is TJSONObject then
Self.Fields[ii].AsString := JObjectData.GetValue(Self.FieldDefs[ii].Name).ToJSON
else if JSONCheck is TJSONArray then
Self.Fields[ii].AsString := JObjectData.GetValue(Self.FieldDefs[ii].Name).ToJSON
else
Self.Fields[ii].AsString := JObjectData.Values[Self.FieldDefs[ii].Name].Value;
JSONCheck.DisposeOf;
end;
Self.Post;
end;
Result := True;
except
on E : Exception do begin
Result := False;
Self.FillError('Error Parsing JSON', E.Message);
end;
end;
finally
if FResult = FObj then
JObjectData.DisposeOf;
if FResult = FArr then
JArrayJSON.DisposeOf;
Self.First;
end;
end;
procedure TFDMemTableHelper.FillError(FMessage, FError : String);
begin
Self.Active := False;
Self.Close;
Self.FieldDefs.Clear;
Self.FieldDefs.Add('status', ftString, 200, False);
Self.FieldDefs.Add('messages', ftString, 200, False);
Self.FieldDefs.Add('data', ftString, 200, False);
Self.CreateDataSet;
Self.Active := True;
Self.Open;
Self.Append;
Self.Fields[0].AsString := FError;
Self.Fields[1].AsString := FMessage;
Self.Post;
end;
end.
I am trying to extract the value from "response.AAPL.results.year_high.data" since there is two value. I have found many examples, but the value bracket in data is all individually identify by a title, mine is not. Does anybody know's how to access the information ?
in Oracle 12
thanks in advance.
create or replace procedure test(p_where in varchar2, p_radius in number, p_room in number)
is
begin
DECLARE
l_param_list VARCHAR2(512);
l_http_request UTL_HTTP.req;
l_http_response UTL_HTTP.resp;
l_response_text VARCHAR2(32767);
l_members WWV_FLOW_T_VARCHAR2;
l_count PLS_INTEGER;
l_list json_list;
obj json := json();
l_json_values apex_json.t_values;
arr json_list := json_list();
l_paths apex_t_varchar2;
BEGIN
l_response_text := '{"response": {"AAPL": {"meta": {"status": "ok"}, "results": {"year_high": {"meta": {"status": "ok"}, "data": [["2016-09-30", 123.8200], ["2016-09-29", 125.0000]]}}}, "MSFT": {"meta": {"status": "ok"}, "results": {"year_high": {"meta": {"status": "ok"}, "data": ["2016-09-30", 58.7000]}}}}}';
/* DBMS_OUTPUT.put_line(l_response_text);*/
apex_json.parse (l_response_text);
/* dbms_output.put_line (apex_json.get_varchar2(p_path => 'count')); */
/* dbms_output.put_line (apex_json.get_number (p_path => 'response.MSFT.results.price.data',p0=>2,p_values =>l_json_values));*/
DBMS_OUTPUT.put_line('----------------------------------------');
DBMS_OUTPUT.put_line('Check elements (members) below a path');
l_members := APEX_JSON.get_members(p_path=>'response.AAPL.results.year_high');
DBMS_OUTPUT.put_line('Members Count : ' || l_members.COUNT);
FOR i IN 1 .. l_members.COUNT LOOP
DBMS_OUTPUT.put_line('Member Item Idx : ' || i);
DBMS_OUTPUT.put_line('Member Name : ' || l_members(i));
END LOOP;
/* This is were I would like to extract the value in the data member*/
DBMS_OUTPUT.put_line('----------------------------------------');
DBMS_OUTPUT.put_line('Employee Information (Loop through array)');
l_count := APEX_JSON.get_count(p_path => 'response.AAPL.results.year_high.data');
DBMS_OUTPUT.put_line('Employees Count : ' || l_count);
FOR i IN 1 .. l_count LOOP
DBMS_OUTPUT.put_line('Employee Item Idx : ' || i);
DBMS_OUTPUT.put_line('Employee Number : ' ||
APEX_JSON.get_varchar2(p_path => 'response.AAPL.results.year_high.data[%d]', p0 => i));
DBMS_OUTPUT.put_line('Employee Name : ' ||
APEX_JSON.get_varchar2(p_path => 'response.AAPL.results.year_high.data[%d]', p0 => i));
END LOOP;
/* dbms_output.put_line (apex_json.get_varchar2 ('response.MSFT.results.year_high.data[%d]', 1));
dbms_output.put_line (apex_json.get_varchar2('response.MSFT.results.year_high.data[%d]', 2));
dbms_output.put_line (apex_json.get_varchar2 ('response.AAPL.results.year_high.data[%d]',1));
dbms_output.put_line (apex_json.get_varchar2('response.AAPL.results.year_high.data[%d]',2));
*/
end;
end test;
First of, it's very weird behavior to have an array of different data types. i.e.: [["2016-09-30", 123.8200], ["2016-09-29", 125.0000]]
Normally you would have an array of dates, array of numbers, array of text. Not mixed.
As you can see the data is an array of arrays. So you need to address that:
declare
json varchar2 (32767)
:= '{"response": {"AAPL": {"meta": {"status": "ok"}, "results": {"year_high": {"meta": {"status": "ok"}, "data": [["2016-09-30", 123.8200], ["2016-09-29", 125.0000]]}}}, "MSFT": {"meta": {"status": "ok"}, "results": {"year_high": {"meta": {"status": "ok"}, "data": ["2016-09-30", 58.7000]}}}}}';
begin
apex_json.parse (json);
dbms_output.put_line ('First value: ' || apex_json.get_varchar2 ('response.AAPL.results.year_high.data[1][1]'));
dbms_output.put_line ('Second value: ' || apex_json.get_number ('response.AAPL.results.year_high.data[1][2]'));
end;
Will output:
First value: 2016-09-30
Second value: 123,82
EDIT:
To use a loop, this why it's bad to mix types in an array. Thankfully get_varchar2 will cast a number to text and not result in an error:
declare
json varchar2 (32767)
:= '{"response": {"AAPL": {"meta": {"status": "ok"}, "results": {"year_high": {"meta": {"status": "ok"}, "data": [["2016-09-30", 123.8200], ["2016-09-29", 125.0000]]}}}, "MSFT": {"meta": {"status": "ok"}, "results": {"year_high": {"meta": {"status": "ok"}, "data": ["2016-09-30", 58.7000]}}}}}';
begin
apex_json.parse (json);
for x in 1 .. nvl (apex_json.get_count ('response.AAPL.results.year_high.data'), -1) loop
for y in 1 .. nvl (apex_json.get_count ('response.AAPL.results.year_high.data[%d]', x), -1) loop
dbms_output.put_line ('First value: ' || apex_json.get_varchar2 ('response.AAPL.results.year_high.data[%d][%d]', x, y));
end loop;
end loop;
end;
Outputs:
First value: 2016-09-30
First value: 123.82
First value: 2016-09-29
First value: 125
This question already has an answer here:
Delphi - Access Violation on Parsing JSON Data # Run Time
(1 answer)
Closed 8 years ago.
I was trying to parse JSON from a website using Delphi XE2 and following code suggested in this thread here. When I implement the code exactly as it appears in the respondents post, it works, but when I implement something almost identical in mine, I get an access violation at a line that read:
LParts:=LJsonObj.Get('parts').JsonValue;
And it fails (when running it) with an AV every time (compiles ok, just won't run). I have simplified my code and below is a snippet that I would expect to work. Afterall, this is a copy and paste of other, working code.
I would be really grateful if someone could have a look below and let me know why mine doesn't work but the code in the referenced post does. I am puzzled largely because when I debug it, it looks like it should be working and there is little debug info to go off of.
program ReadJSONConsoleApp;
{$APPTYPE CONSOLE}
{$R *.res}
uses
DBXJSON,
System.SysUtils;
Const
StrJson=
'{' +
' "response" : [ {' +
' "distributor" : {' +
' "id" : 1538,' +
' "name" : "Arrow Electronics",' +
' "authorized" : true,' +
' "logoUrl" : "this is normally a URL but I cut it out"' +
' },' +
' "parts" : [ {' +
' "manufacturer" : "National Semiconductor",' +
' "part" : "LM741WG/883",' +
' "description" : "OP Amp Single GP ±22V 10-Pin CFPAK Tray",' +
' "price" : [ {' +
' "quantity" : 0,' +
' "price" : 65.0,' +
' "currency" : "USD"' +
' } ],' +
' "stock" : 88,' +
' "lastUpdated" : "2013-11-04 18:27:16 UTC"' +
' }, {' +
' "manufacturer" : "National Semiconductor",' +
' "part" : "LM741W/883",' +
' "description" : "OP Amp Single GP ±22V 10-Pin CPAK Rail",' +
' "price" : [ {' +
' "quantity" : 0,' +
' "price" : 40.5,' +
' "currency" : "USD"' +
' } ],' +
' "stock" : 1464,' +
' "lastUpdated" : "2013-11-04 18:27:16 UTC"' +
' }, {' +
' "manufacturer" : "Texas Instruments",' +
' "part" : "LM741CH",' +
' "description" : "OP Amp Single GP ±18V 8-Pin TO-99 Box",' +
' "price" : [ {' +
' "quantity" : 0,' +
' "price" : 5.22,' +
' "currency" : "USD"' +
' } ],' +
' "stock" : 95,' +
' "lastUpdated" : "2013-11-04 18:27:16 UTC"' +
' }, {' +
' "manufacturer" : "Texas Instruments",' +
' "part" : "LM741CN",' +
' "description" : "OP Amp Single GP ±18V 8-Pin PDIP Rail",' +
' "price" : [ {' +
' "quantity" : 0,' +
' "price" : 0.3633,' +
' "currency" : "USD"' +
' } ],' +
' "stock" : 4320,' +
' "lastUpdated" : "2013-11-04 18:27:16 UTC"' +
' }, {' +
' "manufacturer" : "Texas Instruments",' +
' "part" : "LM741H",' +
' "description" : "OP Amp Single GP ±22V 8-Pin TO-99 Box",' +
' "price" : [ {' +
' "quantity" : 0,' +
' "price" : 5.22,' +
' "currency" : "USD"' +
' } ],' +
' "stock" : 3458,' +
' "lastUpdated" : "2013-11-04 18:27:16 UTC"' +
' }, {' +
' "manufacturer" : "Texas Instruments",' +
' "part" : "LM741H",' +
' "description" : "OP Amp Single GP ±22V 8-Pin TO-99 Box",' +
' "price" : [ {' +
' "quantity" : 0,' +
' "price" : 5.71,' +
' "currency" : "USD"' +
' } ],' +
' "stock" : 0,' +
' "lastUpdated" : "2013-11-04 18:27:16 UTC"' +
' }, {' +
' "manufacturer" : "Texas Instruments",' +
' "part" : "LM741CN/NOPB",' +
' "description" : "OP Amp Single GP ±18V 8-Pin PDIP Rail",' +
' "price" : [ {' +
' "quantity" : 0,' +
' "price" : 0.2977,' +
' "currency" : "USD"' +
' } ],' +
' "stock" : 6486,' +
' "lastUpdated" : "2013-11-04 18:27:16 UTC"' +
' }, {' +
' "manufacturer" : "Texas Instruments",' +
' "part" : "LM741J",' +
' "description" : "OP Amp Single GP ±22V 8-Pin CDIP Rail",' +
' "price" : [ {' +
' "quantity" : 0,' +
' "price" : 7.21,' +
' "currency" : "USD"' +
' } ],' +
' "stock" : 362,' +
' "lastUpdated" : "2013-11-04 18:27:16 UTC"' +
' }, {' +
' "manufacturer" : "Texas Instruments",' +
' "part" : "LM741H/NOPB",' +
' "description" : "OP Amp Single GP ±22V 8-Pin TO-99 Box",' +
' "price" : [ {' +
' "quantity" : 0,' +
' "price" : 5.22,' +
' "currency" : "USD"' +
' } ],' +
' "stock" : 1378,' +
' "lastUpdated" : "2013-11-04 18:27:16 UTC"' +
' }, {' +
' "manufacturer" : "Texas Instruments",' +
' "part" : "LM741J/883",' +
' "description" : "OP Amp Single GP ±22V 8-Pin CDIP Rail",' +
' "price" : [ {' +
' "quantity" : 0,' +
' "price" : 11.8,' +
' "currency" : "USD"' +
' } ],' +
' "stock" : 989,' +
' "lastUpdated" : "2013-11-04 18:27:16 UTC"' +
' }, {' +
' "manufacturer" : "Texas Instruments",' +
' "part" : "LM741H/883",' +
' "description" : "OP Amp Single GP ±22V 8-Pin TO-99 Tray",' +
' "price" : [ {' +
' "quantity" : 0,' +
' "price" : 15.74,' +
' "currency" : "USD"' +
' } ],' +
' "stock" : 4252,' +
' "lastUpdated" : "2013-11-04 18:27:16 UTC"' +
' }, {' +
' "manufacturer" : "Texas Instruments",' +
' "part" : "LM741CHNOPB",' +
' "description" : "OP Amp Single GP ±18V 8-Pin TO-99 Box",' +
' "price" : [ {' +
' "quantity" : 0,' +
' "price" : 5.22,' +
' "currency" : "USD"' +
' } ],' +
' "stock" : 785,' +
' "lastUpdated" : "2013-11-04 18:27:16 UTC"' +
' }' +
' } ]' +
' } ]' +
'}';
procedure ParseJson;
var
LJsonObj : TJSONObject;
LJPair : TJSONPair;
LParts : TJSONValue;
LPart : TJSONValue;
LItem : TJSONValue;
LIndex : Integer;
LSize : Integer;
begin
LJsonObj := TJSONObject.ParseJSONValue(TEncoding.ASCII.GetBytes(StrJson),0) as TJSONObject;
try
LParts:=LJsonObj.Get('parts').JsonValue;
LSize:=TJSONArray(LParts).Size;
for LIndex:=0 to LSize-1 do
begin
LPart := TJSONArray(LParts).Get(LIndex);
LJPair := TJSONPair(LPart);
Writeln(Format('Part Name %s',[LJPair.JsonString.Value]));
for LItem in TJSONArray(LJPair.JsonValue) do
begin
if TJSONPair(LItem).JsonValue is TJSONFalse then
Writeln(Format(' %s : %s',[TJSONPair(LItem).JsonString.Value, 'false']))
else
if TJSONPair(LItem).JsonValue is TJSONTrue then
Writeln(Format(' %s : %s',[TJSONPair(LItem).JsonString.Value, 'true']))
else
Writeln(Format(' %s : %s',[TJSONPair(LItem).JsonString.Value, TJSONPair(LItem).JsonValue.Value]));
end;
end;
finally
LJsonObj.Free;
end;
end;
begin
try
ParseJson;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
Readln;
end.
The call to ParseJSONValue returns nil. The documentation calls this out:
ParseJSONValue returns the JSON value corresponding to the parsed data, or null if the parsing fails.
You fail to check for such a condition, and the access violation is the inevitable consequence. Unless your JSON is pre-validated, you'll want to check its validity.
You can double check this yourself by pasting your JSON into an online validator. For example: http://jsonlint.com/ Do this and you will see that the JSON is not valid.
Here's some valid JSON:
StrJson=
'{' +
' "response" : {' +
' "distributor" : {' +
' "id" : 1538,' +
' "name" : "Arrow Electronics",' +
' "authorized" : true,' +
' "logoUrl" : "this is normally a URL but I cut it out"' +
' },' +
' "parts" : [ {' +
' "manufacturer" : "National Semiconductor",' +
' "part" : "LM741WG/883",' +
' "description" : "OP Amp Single GP ±22V 10-Pin CFPAK Tray",' +
' "price" : [ {' +
' "quantity" : 0,' +
' "price" : 65.0,' +
' "currency" : "USD"' +
' } ],' +
' "stock" : 88,' +
' "lastUpdated" : "2013-11-04 18:27:16 UTC"' +
' }, {' +
' "manufacturer" : "National Semiconductor",' +
' "part" : "LM741W/883",' +
' "description" : "OP Amp Single GP ±22V 10-Pin CPAK Rail",' +
' "price" : [ {' +
' "quantity" : 0,' +
' "price" : 40.5,' +
' "currency" : "USD"' +
' } ],' +
' "stock" : 1464,' +
' "lastUpdated" : "2013-11-04 18:27:16 UTC"' +
' }, {' +
' "manufacturer" : "Texas Instruments",' +
' "part" : "LM741CH",' +
' "description" : "OP Amp Single GP ±18V 8-Pin TO-99 Box",' +
' "price" : [ {' +
' "quantity" : 0,' +
' "price" : 5.22,' +
' "currency" : "USD"' +
' } ],' +
' "stock" : 95,' +
' "lastUpdated" : "2013-11-04 18:27:16 UTC"' +
' }, {' +
' "manufacturer" : "Texas Instruments",' +
' "part" : "LM741CN",' +
' "description" : "OP Amp Single GP ±18V 8-Pin PDIP Rail",' +
' "price" : [ {' +
' "quantity" : 0,' +
' "price" : 0.3633,' +
' "currency" : "USD"' +
' } ],' +
' "stock" : 4320,' +
' "lastUpdated" : "2013-11-04 18:27:16 UTC"' +
' }, {' +
' "manufacturer" : "Texas Instruments",' +
' "part" : "LM741H",' +
' "description" : "OP Amp Single GP ±22V 8-Pin TO-99 Box",' +
' "price" : [ {' +
' "quantity" : 0,' +
' "price" : 5.22,' +
' "currency" : "USD"' +
' } ],' +
' "stock" : 3458,' +
' "lastUpdated" : "2013-11-04 18:27:16 UTC"' +
' }, {' +
' "manufacturer" : "Texas Instruments",' +
' "part" : "LM741H",' +
' "description" : "OP Amp Single GP ±22V 8-Pin TO-99 Box",' +
' "price" : [ {' +
' "quantity" : 0,' +
' "price" : 5.71,' +
' "currency" : "USD"' +
' } ],' +
' "stock" : 0,' +
' "lastUpdated" : "2013-11-04 18:27:16 UTC"' +
' }, {' +
' "manufacturer" : "Texas Instruments",' +
' "part" : "LM741CN/NOPB",' +
' "description" : "OP Amp Single GP ±18V 8-Pin PDIP Rail",' +
' "price" : [ {' +
' "quantity" : 0,' +
' "price" : 0.2977,' +
' "currency" : "USD"' +
' } ],' +
' "stock" : 6486,' +
' "lastUpdated" : "2013-11-04 18:27:16 UTC"' +
' }, {' +
' "manufacturer" : "Texas Instruments",' +
' "part" : "LM741J",' +
' "description" : "OP Amp Single GP ±22V 8-Pin CDIP Rail",' +
' "price" : [ {' +
' "quantity" : 0,' +
' "price" : 7.21,' +
' "currency" : "USD"' +
' } ],' +
' "stock" : 362,' +
' "lastUpdated" : "2013-11-04 18:27:16 UTC"' +
' }, {' +
' "manufacturer" : "Texas Instruments",' +
' "part" : "LM741H/NOPB",' +
' "description" : "OP Amp Single GP ±22V 8-Pin TO-99 Box",' +
' "price" : [ {' +
' "quantity" : 0,' +
' "price" : 5.22,' +
' "currency" : "USD"' +
' } ],' +
' "stock" : 1378,' +
' "lastUpdated" : "2013-11-04 18:27:16 UTC"' +
' }, {' +
' "manufacturer" : "Texas Instruments",' +
' "part" : "LM741J/883",' +
' "description" : "OP Amp Single GP ±22V 8-Pin CDIP Rail",' +
' "price" : [ {' +
' "quantity" : 0,' +
' "price" : 11.8,' +
' "currency" : "USD"' +
' } ],' +
' "stock" : 989,' +
' "lastUpdated" : "2013-11-04 18:27:16 UTC"' +
' }, {' +
' "manufacturer" : "Texas Instruments",' +
' "part" : "LM741H/883",' +
' "description" : "OP Amp Single GP ±22V 8-Pin TO-99 Tray",' +
' "price" : [ {' +
' "quantity" : 0,' +
' "price" : 15.74,' +
' "currency" : "USD"' +
' } ],' +
' "stock" : 4252,' +
' "lastUpdated" : "2013-11-04 18:27:16 UTC"' +
' }, {' +
' "manufacturer" : "Texas Instruments",' +
' "part" : "LM741CHNOPB",' +
' "description" : "OP Amp Single GP ±18V 8-Pin TO-99 Box",' +
' "price" : [ {' +
' "quantity" : 0,' +
' "price" : 5.22,' +
' "currency" : "USD"' +
' } ],' +
' "stock" : 785,' +
' "lastUpdated" : "2013-11-04 18:27:16 UTC"' +
' }' +
' ]' +
' }' +
'}';
Whether or not it's what you want, I cannot be sure.
Plug this constant into your program, and it will advance further, but then fail on the Get('parts') line. That also returns nil and you get another AV when you try to read the JsonValue property.
Not knowing exactly what you are doing, I cannot tell you how to debug the entire code. What you do need to do is to start taking note of the fact that these JSON methods and properties may return nil. So check for that before you attempt to access methods and properties on a nil reference.
My other piece of advice is to put the JSON into a file so that it is easier to work with. Building up constants like this makes debugging very tricky.