0

My environment:

  • RadStudio 10.2 Tokyo
  • Working on Windows 10 (64bit) v1809

I was searching the way to replace JSON value. Then, I came across the following Q and A.

In Delphi:

JoPair.JsonValue.Free;
JoPair.JsonValue := TJSONNumber.Create(123);

Following this, I thought, it would be in C++ Builder

JoPair->JsonValue->Free();
JoPair->JsonValue = new TJSONNumber(123);

However, it caused "access violation" error in ToString();

Instead, I commented out JoPair->JsonValue->Free();, then no problem.

Question:

In C++ Buidler, should I need Free JoPair->JsonValue?

But without Freeing the JsonValue, it may cause memory leak.

source code

Following is the actual code I checked

//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include <DBXJSON.hpp> // for JSON
#include <memory> // for unique_ptr
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------

__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
    String srcFileName = L"test.json";  // source
    String dstFileName = L"out.json";  // destination
    String targetKeySubString = L"hogehoge";  //
    String targetValue = "9";

    // 1. read JSON strings
    std::unique_ptr<TStringList> slread(new TStringList);
    slread->LoadFromFile(srcFileName);

    // 2. replace values for the key (target is checked by substring)
    TJSONObject *jsonObj;
    String jsonKey, jsonValue;
    TJSONPair *pairObj;

    std::unique_ptr<TStringList> slwrite(new TStringList);

    for(int li=0; li < slread->Count; li++) { // file line index
        String jsonText = slread->Strings[li];
        // this is needed for avoiding error caused by the file path treating backslash
        jsonText = StringReplace(jsonText, L"\\", L"\\\\", TReplaceFlags()<<rfReplaceAll);
        //
        jsonObj = dynamic_cast<TJSONObject*>(TJSONObject::ParseJSONValue(jsonText));

        for(int pi=0; pi < jsonObj->Size(); pi++) { // pair index
            pairObj = jsonObj->Get(pi);
            jsonKey = pairObj->JsonString->Value();
            jsonValue = pairObj->JsonValue->Value();

            if (jsonKey.Pos(targetKeySubString) == 0) {
                continue;
            }

            // replace value
            // (ref: https://stackoverflow.com/questions/33426576/delphi-xe7-how-to-change-a-json-value-using-system-json-versus-superobject)
            //
            //pairObj->JsonValue->Free();  // commented out because this causes "access violation" in ToString()
            pairObj->JsonValue = new TJSONString(targetValue);

            // debug
            //ShowMessage(jsonKey + ":" + jsonValue);
        }

        slwrite->Add(jsonObj->ToString());
    }
    jsonObj->Free();

    // 3. output 
    slwrite->SaveToFile(dstFileName);

    ShowMessage(L"Done");

}
//---------------------------------------------------------------------------

Example

{"1_hogehoge":"3", "2_fugafuga":"1","3_hogehoge":"4", "4_fugafuga":"1", "5_hogehoge":"5", "6_fugafuga":"9"}
{"1_hogehoge":"9","2_fugafuga":"1","3_hogehoge":"9","4_fugafuga":"1","5_hogehoge":"9","6_fugafuga":"9"}

Source code (10.2 Tokyo)

I have updated the source code. Still same problem.

I also used ToJSON() instead of ToString() with same error (access violation)`.

I also tried using std::unique_ptr, which caused another error. So I gave up using std::unique_ptr on this topic now (may better be investigated separately).

//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include <System.JSON.hpp>
#include <memory> // for unique_ptr
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------

__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
    String srcFileName = L"test.json";  // source
    String dstFileName = L"out.json";  // destination
    String targetKeySubString = L"hogehoge";  //
    String targetValue = "9";

    // 1. read JSON strings
    std::unique_ptr<TStringList> slread(new TStringList);
    slread->LoadFromFile(srcFileName);

    // 2. replace values for the key (target is checked by substring)
    TJSONObject *jsonObj;
    //std::unique_ptr<TJSONObject> jsonObj(new TJSONObject);
    String jsonKey, jsonValue;
    TJSONPair *pairObj;

    std::unique_ptr<TStringList> slwrite(new TStringList);

    for(int li=0; li < slread->Count; li++) { // file line index
        String jsonText = slread->Strings[li];
        // this is needed for avoiding error caused by the file path treating backslash
        jsonText = StringReplace(jsonText, L"\\", L"\\\\", TReplaceFlags()<<rfReplaceAll);
        //
        jsonObj = dynamic_cast<TJSONObject*>(TJSONObject::ParseJSONValue(jsonText));

        if (jsonObj == NULL) {
            continue;
        }

        for(int pi=0; pi < jsonObj->Count; pi++) { // pair index
            pairObj = jsonObj->Pairs[pi];
            jsonKey = pairObj->JsonString->Value();
            jsonValue = pairObj->JsonValue->Value();

            if (jsonKey.Pos(targetKeySubString) == 0) {
                continue;
            }

            // replace value
            // (ref: https://stackoverflow.com/questions/33426576/delphi-xe7-how-to-change-a-json-value-using-system-json-versus-superobject)
            //
            //pairObj->JsonValue->Free();  // commented out because this causes "access violation" in ToString()

            delete pairObj->JsonValue;

            pairObj->JsonValue = new TJSONString(targetValue);

            // debug
            //ShowMessage(jsonKey + ":" + jsonValue);
        }
        //String res = jsonObj->ToJSON();   // *** access violation ***
        String res = jsonObj->ToString();  // *** access violation ***
        slwrite->Add(res);
        jsonObj->Free();
    }

    // 3. output
    slwrite->SaveToFile(dstFileName);

    ShowMessage(L"Done");

}
//---------------------------------------------------------------------------
sevenOfNine
  • 1,509
  • 16
  • 37
  • I don't know this framework, but might `pairObj->JsonValue` be null? – Neijwiert May 07 '19 at 07:15
  • After Free pairObj->JsonValue, pairObj->JsonValue becomes null, causing access violation, I think. – sevenOfNine May 07 '19 at 07:16
  • 3
    Never directly call `TObject::Free()` in C++, use the `delete` operator instead: `delete JoPair->JsonValue; ... delete jsonObj;` – Remy Lebeau May 07 '19 at 19:34
  • Thank you Remy. I had tried using delete instead of Free() too. But with same result (access violation). – sevenOfNine May 07 '19 at 23:35
  • 1
    @sevenOfNine you are not checking the result of `ParseJSONValue()` or `dynamic_cast` for NULL. Which platform are you running this code on? Is it an ARC-enabled mobile platform? If so, calling `delete pairObj->JsonValue` (or `pairObj->JsonValue->Free()`) will have no effect and can be skipped. Otherwise, you are leaking memory as you create a new `TJSONObject` on each outer loop iteration but are not freeing the `jsonObj` until after the outer loop exits. You need to free it on each outer loop iteration (use `std::unique_ptr` to help you with that, since you are already using it elsewhere). – Remy Lebeau May 08 '19 at 02:58
  • 2
    @sevenOfNine also, why are you using `#include ` in Tokyo? You should be using `#include ` instead. `DBXJSON` hasn't been used since XE5. – Remy Lebeau May 08 '19 at 03:02
  • @RemyLebeau Thank you very much. I am running the program on Windows 10 (64bit) platform using VCL. I will modify to free jsonObj on each outer loop iteration or use `std::unique_ptr` as I use this elsewhere. – sevenOfNine May 08 '19 at 04:00
  • @RemyLebeau Thank you again about the include. I moved project from XE4 to 10.2 Tokyo. So some of the code has old-style implementation. I will modify them accordingly. Firstly from `System.JSON.hpp` instead of `DBXJSON`. – sevenOfNine May 08 '19 at 04:01
  • 2
    "*I also tried using std::unique_ptr, which caused another error*" - because you are not using it correctly. Move the instance inside of the loop and initialize it with the result of `ParseJSONValue()`, eg: `TJSONObject *jsonObj; ... for(int li=0; li < slread->Count; li++) { ... std::unique_ptr jsonValue(TJSONObject::ParseJSONValue(jsonText)); jsonObj = dynamic_cast(jsonValue.get()); ... }` – Remy Lebeau May 08 '19 at 05:49
  • @RemyLebeau I appreciate your comment on `std::unique_ptr`. as you write I could use `std::unique_ptr` with a little modification (definiting jsonValues instead of jsonValue where jsonValue is used as String at the later code). – sevenOfNine May 08 '19 at 06:54

0 Answers0