Nested JSON Objects - json

I am using an API to output a JSON file as such, and so cannot edit the format of the JSON file. Is there any way to be able to iterate through each intraday date, to find the value of open in each date?
I have tried using Object.intraday[0].open however this does not seem to work, as the it is not contained within an array?
I realise that I can use Object.intraday.date.open (where date is e.g "2018-10-19 15:59:00:"); however I want to be able to index to the different times.
{
"symbol": "AAPL",
"stock_exchange_short": "NASDAQ",
"timezone_name": "America/New_York",
"intraday": {
"2018-10-19 15:59:00:" {
"open": "219.49",
"close": "219.23",
"high": "219.61",
"low": "219.19",
"volume": "302415"
},
"2018-10-19 15:58:00:" {
"open": "219.62",
"close": "219.48",
"high": "219.70",
"low": "219.48",
"volume": "173762"
},
....
This is the pascal code that I am using in order to do this, using a test JSON of {"intraday":{"2018-10-1915:59:00":{"open":"23","low":"4"},"2018-10-1915:58:00":{"open":"25","low":"21"}}}
JSONValue := TJSONObject.ParseJSONValue('{"intraday":{"2018-10-1915:59:00":{"open":"23","low":"4"},"2018-10-1915:58:00":{"open":"25","low":"21"}}}');
j:=0;
begin
if JSONVAlue is TJSONObject then
begin
// Get the quote and low values
quote := JSONValue.GetValue<string>('intraday[0]');
low := JSONValue.GetValue<string>('intraday.low');
Memo1.Lines.Add(quote + ': ' + low);
j := j+1;
end;
end;

procedure TfmMain.ParseIntraDay(AMemo: TMemo);
var
LObject, LIntraDayObj: TJSONObject;
LEnumerator: TJsonPairEnumerator;
LQuote, LLow: String;
begin
LObject := TJSONObject.ParseJSONValue('{"intraday":{"2018-10-19 15:59:00":{"open":"23","low":"4"},"2018-10-19 15:58:00":{"open":"25","low":"21"}}}') as TJSONObject;
try
LIntraDayObj := LObject.GetValue('intraday') as TJSONObject; //{"2018-10-19 15:59:00":{"open":"23","low":"4"},"2018-10-19 15:58:00":{"open":"25","low":"21"}}
LEnumerator := LIntraDayObj.GetEnumerator;
while LEnumerator.MoveNext do
begin
LQuote := LEnumerator.Current.JsonString.Value;
LLow := (LEnumerator.Current.JsonValue As TJsonObject).Values['low'].Value;
AMemo.Lines.Add(String.Format('%s: %s', [LQuote, LLow]));
end;
finally
LObject.Free;
end;
end;
Should give the following output in the memo:
2018-10-19 15:59:00: 4
2018-10-19 15:58:00: 21
Youcan use this as a basis for what you want.

Related

Call remote server through oracle apex

How to call external server or remote server from Oracle APEX using various restful services kindly let me know.
Thanks & Regards,
Yokes G
One way to do it is to set up a Rest Data Source entry. I am using Oracle Apex 20.2.
eg called myRest
using Simple HTTP
Remote Server: Server name without https: include the endpoint
JSON Operation: GET
Parameters: the header parameters
It is very easy to copy and paste this from PostMan.
Then you can use the following type of code to access this REST service
declare
l_params apex_exec.t_parameters;
begin
apex_exec.add_parameter( l_params, 'content', 'hello david' );
apex_exec.add_parameter( l_params, 'to', '123456' );
apex_exec.execute_rest_source(
'myRest',
'GET',
NULL,
l_params );
end;
Note that I couldn't get the JSON to work properly using Rest Data Sources and so I use make_rest_request instead as follows:
declare
l_host constant varchar2(200) := 'https://platform.xxx.com';
l_host_path constant varchar2(200) := l_host || '/v1/message';
l_clob clob;
l_body clob;
begin
l_body := '{
"messages": [
{
"channel": "xxx",
"to": "123",
"content": "Hello from APEX"
}
]
}';
apex_web_service.g_request_headers(1).name := 'Content-Type';
apex_web_service.g_request_headers(1).value := 'application/json';
apex_web_service.g_request_headers(2).name := 'Authorization';
apex_web_service.g_request_headers(2).value := 'yourkey';
l_clob := apex_web_service.make_rest_request(
p_url => l_host_path,
p_http_method => 'POST',
p_body => l_body);
end;
This second method is nice as then everything is in one place and you don't need to reference the REST Data Sources.

How to hide "ownsObjects" and "listHelper" TObjectList's properties from a Json using Delphi (Rest.JSON)?

I'm using Delphi Tokyo and trying to convert an object into a json using TJson. ObjectToJsonString method present in Rest.Json.
A simple object with simple properties such as String or Integer it's ok, but when a add a property TObjectList, the json is poluted with the properties "ownsObjects" and "listHelper", but the WebService doesn't accept these fields. How can I "hide" it from a Json?
You can put the JsonReflect attribute on fields and control how they are being serialized.
Here is some example code of how to write your own specialized "serialize this object list as array" attribute that takes care of that - just add the unit to the uses and add [SerializeObjectList] to your field.
unit CollectionsReflect;
interface
uses
Generics.Collections,
REST.JsonReflect;
type
SerializeObjectListAttribute = class(JsonReflectAttribute)
constructor Create;
end;
implementation
uses
Rtti;
type
TListOfObjectInterceptor = class(TJSONInterceptor)
function ObjectsConverter(Data: TObject; Field: string): TListOfObjects; override;
end;
{ TListOfObjectInterceptor }
function TListOfObjectInterceptor.ObjectsConverter(Data: TObject;
Field: string): TListOfObjects;
var
ctx: TRttiContext;
list: TList<TObject>;
begin
list := TList<TObject>(ctx.GetType(Data.ClassInfo).GetField(Field).GetValue(Data).AsObject);
Result := TListOfObjects(list.List);
SetLength(Result, list.Count);
end;
{ SerializeObjectListAttribute }
constructor SerializeObjectListAttribute.Create;
begin
inherited Create(ctObjects, rtObjects, TListOfObjectInterceptor);
end;
end.
Unfortunately the opposite way does not work this way as there seems to be a bug inside of TJSONUnMarshal.PopulateFields that causes an AV when trying to populate the list from the json string.
// How to hide “ownsObjects” and “listHelper” TObjectList's properties from a Json
type
TSameClass = class(...)
....
public
...
function GetAsJson: string;
...
end;
...
// метод любого класса Txxxxx для получения его json, у которого, для всех его
// переменных с типом TObjectList, будут очищены “ownsObjects” и “listHelper” свойства
function TSameClass.GetAsJson: string;
procedure ClearJsonObjectList(AJson: TJSONObject);
var je: TJSONObject.TEnumerator;
begin
// проходим по всему дереву json и удаляем ненужные нам пары
je := AJson.GetEnumerator();
while je.MoveNext() do
if je.Current.JsonValue is TJSONObject then
// рекурсивный вызов
ClearJsonObjectList(je.Current.JsonValue as TJSONObject)
else
// если есть этот Pair, то есть и другой
if Assigned(AJson.RemovePair('listHelper')) then
AJson.RemovePair('ownsObjects');
end;
var j: TJSONObject;
begin
// получаем json класса, в котором могут быть или не быть TObjectList с ненужными нам парами
j := TJson.ObjectToJsonObject(Self);
// в этой процедуре очищаем полученный json от этих пар
ClearJsonObjectList(j);
// возвращаем результат в виде строки json
Result := j.ToString;
end;
(*
// example
// json before ClearJsonObjectList --------------->
{
"content":{
"checkClose":{
"payments":{
"ownsObjects":true, <<-- must be removed
"listHelper":[ <<-- must be removed
]
},
...
},
"positions":{
"ownsObjects":true, <<-- must be removed
"listHelper":[ <<-- must be removed
]
},
...
},
...
}
// json after ClearJsonObjectList --------------->
{
"content":{
"checkClose":{
"payments":{
},
...
},
"positions":{
},
...
},
...
}
*)
I recommend that you create an array of objects instead of a list, if not generally, then for the purpose of serialization to JSON. This works much better with JSON, both reading and writing. Just make sure to handle the memory involved, i.e. free the objects in the list, something like this:
procedure TSomeContainerObject.BeforeDestruction;
var
o: TSomeObjectInTheArray;
begin
for o in fObjectArray do
o.Free;
inherited;
end;
I usually put this in my *.dpr file just to make sure:
begin
{$IFDEF Debug}
ReportMemoryLeaksOnShutdown := true;
{$ENDIF}
// The rest of your startup code here
end.
function TSrvMethContab.GetConecctions: TJSONObject;
var
lContext : TDbContext ;
lLista : TList<TConexions>;
jResult : TJSONObject;
begin
lContext := TDbContext.Create(strConexion);
try
lLista := lContext.Select<TConexions>();
jResult := TJson.ObjectToJsonObject(lLista);
jResult.RemovePair('listHelper');
Result := jResult;
finally
lContext.Free;
end;
end;

Extract specific values from JSON array

I have a JSON response that is formatted this way:
- Client 1
- Date: 15.07.2017
- Name: John
- URL: www.google.com
- Client 2
- Date: 15.07.2017
- Name: Jane
- URL: www.google.com
- Client N...
How could I extract only the Name & URL value from each client so I could add them to a listbox for example? Also please note that "Client 1" could be named otherwise, like "User 1" or just "1", that's not important, but the code should extract the values regardless of the parent object name.
PS: Sorry for missleading, the JSON format above was pseudo-code from memory, the actual format is:
[
{
"date":"xxx",
"name":"xxx",
"url":"xxx"
},
{
"date":"xxx",
"name":"xxx",
"url":"xxx"
},
{
"date":"xxx",
"name":"xxx",
"url":"xxx"
}
]
Answer in case anybody is looking.
procedure Answer;
var
JSON: string;
ClientItem: TJSONValue;
ClientList: TJSONArray;
ListBoxItem: TListBoxItem;
begin
JSON := TFile.ReadAllText('.\your-file.json');
ClientList := TJSONObject.ParseJSONValue(JSON) as TJSONArray;
if Assigned(ClientList) then
try
ListBox.Items.BeginUpdate;
try
for ClientItem in ClientList do
begin
ListBoxItem := TListBoxItem.Create(ListBox);
ListBoxItem.StyleLookup := 'CustomListbox';
ListBoxItem.StylesData['URL'] := ClientItem.GetValue<string>('url');
ListBoxItem.StylesData['Name'] := ClientItem.GetValue<string>('name');
Listbox.AddObject(ListBoxItem);
end;
finally
Listbox.Items.EndUpdate;
end;
finally
ClientList.Free;
end;
end;

Delphi / SuperObject - Accessing subnodes away returning NIL

I have the following JSON from server:
{
"SuccessResponse": {
"Head": {
"RequestId": "",
"RequestAction": "GetMultipleOrderItems",
"ResponseType": "Orders",
"Timestamp": "2016-05-10T15:13:06-0300"
},
"Body": {
"Orders": {
"Order": [
{
"OrderId": "457634",
"OrderNumber": "256176682",
"OrderItems": {
"OrderItem": {
"OrderItemId": "712893",
"ShopId": "14690930",
"OrderId": "457634",
...
I'm using the following code to access this values:
procedure TForm1.GetOrdersPendingItems;
var
mydata : string;
obj, orderObj: ISuperObject;
orderArray: TSuperArray;
begin
mydata := GetURLAsString(GenerateApiUrl('GetMultipleOrderItems', 'OrderIdList', '[457634,457817]'));
obj := SO(mydata);
orderObj := obj['SuccessResponse.Body.Orders.Order'];
end;
With this code, if I use a simple Label1.Caption := orderObj.AsString;, it show me this:
"OrderId": "457634",
"OrderNumber": "256176682",
"OrderItems": {
"OrderItem": {
"OrderItemId": "712893",
"ShopId": "14690930",
"OrderId": "457634",
...
By the logic, the values inner of OrderItem can be access like this: orderObj['OrderItems.OrderItem'];, but if I try to access a "easy" value like OrderId, that is the first element, using orderObj['OrderId']; it returns nil and the same happens with all nodes of the orderObj...
So, the values in the orderObj.AsString can't be accessed to convert into variable...
There are a way to access the value inner of OrderItem? My objective is convert the values of OrderItem into a ClientDataSet using the following code:
orderArray := orderObj.AsArray;
TJSONDB.JsonToClientDataSet(orderArray, cdsOrdersItems);
Thanks!
Here you mention this:
By the logic, the values inner of OrderItem can be access like this: orderObj['OrderItems.OrderItem'];
This would work, indeed.But right after you wrote this contradicting the last sentence:
but if I try to access a "easy" value like OrderId, that is the first element, using orderObj['OrderId'];
By the logic, as you say, to access the values you could do:
orderObj['OrderItems.OrderItem.OrderId'];
and not orderObj['OrderId']; directly.

PL/JSON Message Lists

I working on parsing a json string stored in a table CLOB in oracle 11g. This process is part of a long parsing routine that parses the data and stores the values in another table and I've just noticed that part of my data is not getting out. The json parses and validates with JSONLint. So I've simplified the parsing to try and find out where I'm going wrong.
So my json coming out my table looks like this.
{
"JSON_data": {
"plant_id": "3006",
"transmit_time": "2015-12-18 11:57:45",
"messages": [{
"work_msg": {
"msg_time": "2015-06-23 04:54:17",
"trigger_type": "interval",
"vert_correction": 358.3,
"ch_latitude": 37.916302,
"ch_longitude": -87.487365,
"ch_heading": 212.3,
"ch_cable_port": 1029.79,
"ch_cable_stbd": 348.63,
"ch_depth": -27.03,
"slurry_velocity": 25.71,
"slurry_density": 1.02,
"ch_rpm": 205.49,
"ch_psi": 540.89,
"prod_instantaneous": 0,
"prod_cumulative": 1216.100000,
"outfall_latitude": 37.915967,
"outfall_longitude": -87.484369,
"outfall_heading": 120.7,
"pump_entries": [{
"pump_name": "main",
"vacuum": 12.73,
"outlet_psi": 22.88
}],
"spud_entries": [{
"position": 6
}]
},
"pipe_length_event": {
"msg_time": "2015-06-23 04:54:17",
"length_floating": 970
}
}]
}
}
My parsing is correctly finding and doing its thing with the 'work_msg' data. It's the 'pipe_length_event' data that I'm not getting to. Below is my simplified pl/sql procedure.
DECLARE
vCONTENT CLOB;
v_parent_json json;
v_json_message_list json_list;
v_json_message_list_value json_value;
v_parent_json_value json_value;
BEGIN
SELECT CONTENT INTO vCONTENT FROM SJM_TEMP4;
v_parent_json := json(vCONTENT);
v_parent_json := json(v_parent_json.get(1));
v_json_message_list := json_list(v_parent_json.get('messages'));
DBMS_OUTPUT.PUT_LINE(v_json_message_list.count);
for message_loop_counter in 1 ..v_json_message_list.count loop
v_parent_json_value := json(v_json_message_list.get(message_loop_counter)).get(1);
DBMS_OUTPUT.PUT_LINE(v_parent_json_value.mapname);
END LOOP;
END;
My dbms_output first gives me a sub-list count of 1. Not 2 so my parsing is not even recognizing the "pipe_length_event" as a sub-list of "messages".
How do I get "pipe_length_event" data using this procedure? I'm almost certain this was working in the past so my first thought is that the json is formatted differently. Is the json ill-formatted?
Thanks in advance.
FOUND IT!!
The issue is in fact the JSON formatting. Below is the correct format. The "work_msg" list was not closed so "pipe_length_event" list was not recognized.
{
"JSON_data": {
"plant_id": "3006",
"transmit_time": "2015-12-18 11:57:45",
"messages": [{
"work_msg": {
"msg_time": "2015-06-23 04:54:17",
"trigger_type": "interval",
"vert_correction": 358.3,
"ch_latitude": 37.916302,
"ch_longitude": -87.487365,
"ch_heading": 212.3,
"ch_cable_port": 1029.79,
"ch_cable_stbd": 348.63,
"ch_depth": -27.03,
"slurry_velocity": 25.71,
"slurry_density": 1.02,
"ch_rpm": 205.49,
"ch_psi": 540.89,
"prod_instantaneous": 0,
"prod_cumulative": 1216.100000,
"outfall_latitude": 37.915967,
"outfall_longitude": -87.484369,
"outfall_heading": 120.7,
"pump_entries": [{
"pump_name": "main",
"vacuum": 12.73,
"outlet_psi": 22.88
}],
"spud_entries": [{
"position": 6
}]
}
}, {
"pipe_length_event": {
"msg_time": "2015-06-23 04:54:17",
"length_floating": 970
}
}]
}
}