C++ Builder 10.2 Tokyo > JSON > 複数のValueを置換する | 問題点 > 意図しない置換 (連鎖的な失敗)


動作環境
RAD Studio 10.2 Tokyo Update 3

処理概要

  • JSONオブジェクトのValueを置換したい
    • 用途: 変換元と変換先の文字列が渡されたとき、それに基づきJSONの中身を書換える

方針

  • ToString()した文字列に対して置換をする
  • その結果をJSONオブジェクトとして戻す

実装

Unit1.h
//---------------------------------------------------------------------------

#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <Vcl.Controls.hpp>
#include <Vcl.StdCtrls.hpp>
#include <Vcl.Forms.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:    // IDE で管理されるコンポーネント
    TButton *Button1;
    TMemo *Memo1;
    void __fastcall Button1Click(TObject *Sender);
    void __fastcall FormCreate(TObject *Sender);
private:    // ユーザー宣言
    TJSONObject * __fastcall JSON_replaceStrings(TJSONObject *srcPtr, String fromStr, String toStr);
public:     // ユーザー宣言
    __fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif

Unit1.cpp
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include <DBXJSON.hpp>
#include <memory>
#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)
{
    TJSONObject *asrc, *adst;

    asrc = new TJSONObject();

    asrc->AddPair(L"name", L"John Smith");
    asrc->AddPair(L"age", L"33");
    asrc->AddPair(L"place", L"Sydney");
    Memo1->Lines->Add(asrc->ToString());

    String fromstr = "John Smith,33";
    String tostr = "7of9,79";

    adst = JSON_replaceStrings(asrc, fromstr, tostr);

    if (adst != NULL) {
        Memo1->Lines->Add(adst->ToString());
    }

    delete asrc;
    delete adst;
}

TJSONObject * __fastcall TForm1::JSON_replaceStrings(TJSONObject *srcPtr, String fromStr, String toStr)
{
    // 1. parse csv string
    std::unique_ptr<TStringList> fromSL(new TStringList);
    fromSL->StrictDelimiter = true;
    fromSL->DelimitedText = ',';
    fromSL->DelimitedText = fromStr;
    std::unique_ptr<TStringList> toSL(new TStringList);
    toSL->StrictDelimiter = true;
    toSL->DelimitedText = ',';
    toSL->DelimitedText = toStr;

    if (fromSL->Count != toSL->Count) {
        return NULL;  // error
    }

    // 2. replace values
    String replaced = srcPtr->ToString();
    for(int idx=0; idx<fromSL->Count; idx++) {
        String afrom = fromSL->Strings[idx];
        String ato = toSL->Strings[idx];
        replaced = StringReplace(replaced, afrom, ato, TReplaceFlags()<<rfReplaceAll);
    }

    return dynamic_cast<TJSONObject*>(TJSONObject::ParseJSONValue(replaced));
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
    Memo1->Lines->Clear();
}
//---------------------------------------------------------------------------

実行例

問題点

意図しない置換 (連鎖的な失敗)

(1,2,3,4,5)のようなValueを持つ5つのKeyを持つJSONオブジェクトがあるとする。

(2,3,4,5,6)に置換するとき、StringReplace()を逐次実施すると'(6,6,6,6,6)`になってしまう場合がある。

  1. (1,2,3,4,5)
  2. (2,2,3,4,5)
  3. (3,3,3,4,5)
  4. (4,4,4,4,5)
  5. (5,5,5,5,5)
  6. (6,6,6,6,6)