How to get ALL 'id' member values from a generic JSON. Without knowing structure of it. Because its very complex and it has a lot of sub objects. It has to loop through all the sub objects.
Again for people that keep on asking where is the example JSON. My question is about how to extract a member value in my case "id" from any generic JSON that has this member inside.
If you don't know the structure of the JSON you receive from somewhere, it is important to note that JSON is "simply" a composite pattern and you can traverse it like any other composite structure. The following example traverse the complete structure in a JSON text and prints the path of any member named 'id'.
procedure ParseJSON;
var
JSONText: string;
JSON: ISuperObject;
begin
// Retrieve JSON as a string into JSONText variable any way you like.
JSON := SO(JSONText);
ProcessObject(JSON.AsObject);
end;
procedure ProcessObject(const aAsObject: TSuperTableString; const aPrefix: string = '');
var
Names: ISuperObject;
Name: string;
Items: ISuperObject;
Item: ISuperObject;
idx: Integer;
Value: string;
ArrayItem: ISuperObject;
begin
if Assigned(aAsObject) then
begin
Names := aAsObject.GetNames;
Items := aAsObject.GetValues;
for idx := 0 to Items.AsArray.Length - 1 do
begin
Name := Names.AsArray[idx].AsString;
Item := Items.AsArray[idx];
if Item.DataType = stObject then
Value := '<Object>'
else if Item.DataType = stArray then
Value := '<Array>'
else
Value := Item.AsString;
if SameText(Name, 'id') then
WriteLn(Format('%s: %s', [aPrefix + Name, Value]));
if Item.DataType = stArray then
for ArrayItem in Item do
ProcessObject(ArrayItem.AsObject, aPrefix + Name + '.');
if Item.DataType = stObject then
ProcessObject(Item.AsObject, aPrefix + Name + '.');
end;
end;
end;
Related
How to get JSON, which contains only Name and Flags?
TUser = class
private
FName: string;
FFlags: UInt32;
FFields: UInt32;
FCreated: TDateTime;
public
property Name: string read FName write FName;
property Flags: UInt32 read FFlags write FFlags;
property Fields: UInt32 read FFields write FFields;
property Created: TDateTime read FCreated write FCreated;
constructor Create;
end;
Usually is used all fields of this class:
var
User: TUser
sJson: string;
sJson := User.AsJson;
but sometimes I needed a JSON with Name and Flags fields only.
Currently, to get such JSON I use such code:
var
User: TUser
usr: ISuperObject;
sJson: string;
usr := SO(User.AsJson);
usr.Remove('Fields');
usr.Remove('Created');
sJson := usr.AsJSON;
But I think is not optimal code (actually in real code I have 15 fields and need to remove 12). How to do that faster?
Updated (another method):
May be this will be more faster and useful for my purpose?
usr := SO('');
usr.S['Name'] := User.Name;
usr.I['Flags'] := User.Flags;
sJson := usr.AsJSON;
Thanks to the #NasreddineGalfout I'm found that is it possible with Neon JSON library. With INeonConfiguration I can select public or published or protected (or any combination) property fields that should be serialized. And is it what I need. Moreover deserializing with Neon is 2x much faster than with XSuperObject.
type
TUser = class
private
FName: string;
FFlags: UInt32;
FFields: UInt32;
FCreated: TDateTime;
public
property Name: string read FName write FName;
property Flags: UInt32 read FFlags write FFlags;
published
property Fields: UInt32 read FFields write FFields;
property Created: TDateTime read FCreated write FCreated;
constructor Create;
end;
function MyToJson(User: TUser): string;
var
Config: INeonConfiguration;
LJSON: TJSONValue;
LWriter: TNeonSerializerJSON;
begin
Config := TNeonConfiguration.Default.SetVisibility([mvPublic{, mvPublished}]);
LWriter := TNeonSerializerJSON.Create(Config);
try
LJSON := LWriter.ObjectToJSON(User);
try
Result := TJSONUtils.ToJSON(LJSON);
finally
LJSON.Free;
end;
finally
LWriter.Free;
end;
end;
procedure MyFromJson(var User: TUser; const AJson: string);
var
Config: INeonConfiguration;
LJSON: TJSONValue;
LReader: TNeonDeserializerJSON;
begin
LJSON := TJSONObject.ParseJSONValue(AJson);
if not Assigned(LJSON) then
Exit;
Config := TNeonConfiguration.Default.SetVisibility([mvPublic{, mvPublished}]);
try
LReader := TNeonDeserializerJSON.Create(Config);
try
LReader.JSONToObject(User, LJSON);
finally
LReader.Free;
end;
finally
LJSON.Free;
end;
end;
I have a JSON string which comes from Amazon. (https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/index.json) The top of the JSON looks like this...
I need to retrieve a list of JUST the offerCode and versionIndexUrl values. I can read an array of offers, but the key of each offer is different, so I can't use the NAME(comprehend, AmazonMWAA, etc). I have tried using element [0] but I get an AV. Here is my relevant code...
procedure Load_AWS_Services;
var
json: string;
idx: Integer;
obj: TJSONObject;
j_array: TJSONArray; // the array of all lineitems/offers
lineItem : TJSONObject;
ServiceEntry: TJSONPair;
begin
try
JSON := DownloadFromURLasString('https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/index.json');
obj := TJSONObject.ParseJSONValue(JSON) as TJSONObject;
try
// Now parse the data...
j_array := TJSONArray(obj.Get('offers').JsonValue);
// Now loop through each individual item
for idx := 0 to pred(j_array.size) do
begin
lineItem := TJSONObject(j_array.Get(idx));
Main.Memo1.Lines.Add(lineItem.ToString); // this shows each offer...so good to this point
ServiceEntry := lineItem.Pairs[0];
ShowMessage(ServiceEntry.Value); // AV here
What do I need to change in my last 2 lines to read INSIDE the 'offers'?
The offers field is a JSON object, not a JSON array (had you used the as operator for the TJSONArray cast, you would have gotten an EInvalidCast exception raised).
Try this instead:
procedure Load_AWS_Services;
var
json: string;
idx: Integer;
j_val: TJSONValue;
j_obj: TJSONObject;
j_pair: TJSONPair;
offers: TJSONObject;
lineItem : TJSONObject;
begin
try
json := DownloadFromURLasString('https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/index.json');
j_val := TJSONObject.ParseJSONValue(json);
try
j_obj := j_val as TJSONObject;
// Now parse the data...
offers := j_obj.GetValue('offers') as TJSONObject;
// Now loop through each individual item
for idx := 0 to pred(offers.Count) do
begin
j_pair := offers.Get(idx);
Main.Memo1.Lines.Add(j_pair.JsonString.Value);
lineItem := j_pair.JsonValue as TJSONObject;
ShowMessage(lineItem.GetValue('offerCode').Value);
ShowMessage(lineItem.GetValue('versionIndexUrl').Value);
...
end;
finally
j_val.Free;
end;
except
...
end;
end;
How can an array of record be stored in JSON via SuperObject library. For example..
type
TData = record
str: string;
int: Integer;
bool: Boolean;
flt: Double;
end;
var
DataArray: Array[0..100] of TData;
Just use the superobject Marshalling TSuperRTTIContext
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
superobject,
System.SysUtils;
type
TData = record
str : string;
int : Integer;
bool : Boolean;
flt : Double;
end;
TDataArray = Array [0 .. 100] of TData;
procedure Test;
var
DataArray : TDataArray;
so : ISuperObject;
ctx : TSuperRttiContext;
begin
ctx := TSuperRttiContext.Create;
try
so := ctx.AsJson<TDataArray>( DataArray );
finally
ctx.Free;
end;
Writeln( so.AsJson );
end;
begin
try
Test;
except
on E : Exception do
Writeln( E.ClassName, ': ', E.Message );
end;
ReadLn;
end.
Make it a string first.
Your array:
//Array[0] := 'Apple';
//Array[1] := 'Orange';
//Array[2] := 'Banana';
myArrayAsStr := '"MyArray": [{ "1": "' + Array[0] +'", "2": "' + Array[1] +'"}';
Then you can just make it into JSON with SO(myArrayAsStr)
You can always generate your array as string in a different procedure but I think thats the way to do it.
Ill keep checking if there is an easier way ;)
EDIT:
SuperObject also has the following function:
function SA(const Args: array of const): ISuperObject; overload;
You will be able to convert that to a string again and add it in the total JSON string.
I have to place a JSON string to a HTTP request's body. One of the string value must be a JSON array. This is how I tried:
uses
DBXJSON;
const
cContent = 'hello world';
var
LJSONObject: TJSONObject;
x: TBytes;
i: integer;
Temp: string;
begin
LJSONObject:= TJSONObject.Create;
LJSONObject.AddPair('id1', 'value1');
LJSONObject.AddPair('id2', 'value2');
LJSONObject.AddPair('id2', 'value3');
x:= TEncoding.ANSI.GetBytes(cContent);
Temp:= '';
for i := 0 to Length(x) - 1 do
Temp:= Temp + IntToStr(x[i]) + ',';
Delete(Temp, Length(Temp), 1);
LJSONObject.AddPair('id4', '[' + Temp + ']');
ShowMessage(LJSONObject.ToString);
end;
This one is not working, because the value will be encapsulated in double quotes. What is the proper way to pass an array value to the JSONObject?
You are passing a string rather than an array. Hence the result you observe. As a rule, if you find yourself assembling the JSON manually, you are doing it wrong.
Pass an array:
var
arr: TJSONArray;
b: Byte;
....
arr := TJSONArray.Create;
for b in TEncoding.ANSI.GetBytes(cContent) do
arr.Add(b);
LJSONObject.AddPair('id4', arr);
How can an array of record be stored in JSON via SuperObject library. For example..
type
TData = record
str: string;
int: Integer;
bool: Boolean;
flt: Double;
end;
var
DataArray: Array[0..100] of TData;
Just use the superobject Marshalling TSuperRTTIContext
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
superobject,
System.SysUtils;
type
TData = record
str : string;
int : Integer;
bool : Boolean;
flt : Double;
end;
TDataArray = Array [0 .. 100] of TData;
procedure Test;
var
DataArray : TDataArray;
so : ISuperObject;
ctx : TSuperRttiContext;
begin
ctx := TSuperRttiContext.Create;
try
so := ctx.AsJson<TDataArray>( DataArray );
finally
ctx.Free;
end;
Writeln( so.AsJson );
end;
begin
try
Test;
except
on E : Exception do
Writeln( E.ClassName, ': ', E.Message );
end;
ReadLn;
end.
Make it a string first.
Your array:
//Array[0] := 'Apple';
//Array[1] := 'Orange';
//Array[2] := 'Banana';
myArrayAsStr := '"MyArray": [{ "1": "' + Array[0] +'", "2": "' + Array[1] +'"}';
Then you can just make it into JSON with SO(myArrayAsStr)
You can always generate your array as string in a different procedure but I think thats the way to do it.
Ill keep checking if there is an easier way ;)
EDIT:
SuperObject also has the following function:
function SA(const Args: array of const): ISuperObject; overload;
You will be able to convert that to a string again and add it in the total JSON string.