Search code examples
jsondelphiinno-setuppascalscriptwidestring

Read specific string from every object of JSON file in Inno Setup


I work with Inno JSON Config (like in Pascal Script fails to retrieve wide string from a custom DLL) to read a simple JSON file named items.json:

{
    "Item_0":{
            "Key_1": "String 1",
            "Key_2": "1",
            "Key_3": "True"
    },
    "Item_1":{
            "Key_1": "String 2",
            "Key_2": "2",
            "Key_3": "False"
    }
}

Here is my code :

[Files]
Source: "JSONConfig.dll";            DestDir: "{tmp}\src";          Flags: dontcopy;       
Source: "json_lists\*";              DestDir: "{tmp}\json_lists";   Flags: dontcopy;

[Code]
var
  ItemList: array of WideString;

function JSONQueryString(FileName, Section, Key, Default: WideString;
  var Value: WideString; var ValueLength: Integer): Boolean;
  external 'JSONQueryString@files:jsonconfig.dll stdcall';

procedure AppendItem(var ItemArray: array of WideString; Item: WideString);
begin
  SetLength(ItemArray, GetArrayLength(ItemArray) + 1);
  ItemArray[GetArrayLength(ItemArray) - 1] := Item;
end;
  
function ParseJson(Filename: WideString; Item: String; Key: String; var ItemArray: array of WideString): Boolean;
var
  StrValue: WideString;
  StrLength: Integer;
  ItemNumber: Integer;
  Path: WideString;
begin  
  SetLength(StrValue, 32);
  StrLength := Length(StrValue);

  result := False;
  Path := ExpandConstant('{tmp}'+ '\json_lists\' + Filename);
  ItemNumber := 0;
  while JSONQueryString(Path, ExpandConstant(Item + '_' + IntToStr(ItemNumber)), Key, 'Default', StrValue, StrLength) do
  begin
    ItemNumber := ItemNumber + 1;
    AppendItem(ItemArray, StrValue);
    result := True;
  end;
end;

function NextButtonClick(CurPageID: Integer): Boolean;
var
  I: Integer;
  PathForNextFile: WideString;
begin
  if CurPageID = wpWelcome then
  begin
    if GetArrayLength(ItemList) = 0 then begin
      ExtractTemporaryFiles('{tmp}\json_lists\*');
      if ParseJson('items.json','Item', 'Key_1', ItemList) then begin
        for I := 0 to GetArrayLength(ItemList) - 1 do begin
          PathForNextFile := ItemList[I] + '.json';
          Log(PathForNextFile);     //**
        end;
      end else begin
        Log('Error while parsing')
      end;
    end;
  end;
  result := True;
end;

I have two issues at //** in the code (line Log(PathForNextFile);):

  • The Log only prompts 'String' without the following number (normally 0 or 1);
  • The Log doesn't prompt either the suffix '.json'. It is quite problematic because I need the variable PathForNextFile with the suffix '.json' to parse another file.

I don't know why this happens, any help is welcome!

Ideally I'd like to deal only with String and not WideString, so should I code a PowerShell script which, when called in Inno Setup, returns the right info as a String, if it is possible? I tried to use koldev's JsonParser Library but I couldn't figure out how it worked, even with How to parse a JSON string in Inno Setup? So yet again any help is welcome!


Solution

  • Even the original code from Inno Setup: Working with JSON returns the value truncated. The comment there by @yuval from 2015 (!) says it:

    Also JSONQueryString never retrieves the last character


    I recommend you to use JsonParser library.

    The following is an equivalent of your code implemented using JsonParser library (and my convenience functions from How to parse a JSON string in Inno Setup?):

    function ParseJsonFile(
      Filename: string; Item: string; Key: string; var ItemArray: array of string):
      Boolean;
    var
      JsonLines: TStringList;
      JsonParser: TJsonParser;
      ItemNumber: Integer;
      JsonRoot, ItemObject: TJsonObject;
      StrValue: TJsonString; // = WideString = string
      ItemKey: string;
    begin
      JsonLines := TStringList.Create;
      JsonLines.LoadFromFile(Filename);
    
      Result := False;
      if ParseJsonAndLogErrors(JsonParser, JsonLines.Text) then
      begin
        JsonRoot := GetJsonRoot(JsonParser.Output);
        ItemNumber := 0;
        while True do
        begin
          ItemKey := Format('%s_%d', [Item, ItemNumber]);
          if FindJsonObject(JsonParser.Output, JsonRoot, ItemKey, ItemObject) and
             FindJsonString(JsonParser.Output, ItemObject, Key, StrValue) then
          begin
            Inc(ItemNumber);
            SetLength(ItemArray, GetArrayLength(ItemArray) + 1);
            ItemArray[GetArrayLength(ItemArray) - 1] := StrValue;
            Result := True;
          end
            else Break;
        end;
      end;
    
      ClearJsonParser(JsonParser);
    end;