2

I have a Delphi program written in Alexandria 11.1 that needs to send a JSON file to a service that is written in C#. If the JSON structure is static, I create a model class and use the native TJson.ObjectToJsonString of REST.Json to convert it to JSON string.

For example, a static JSON like:

{
    "pair1": "value1",
    "pair2": "value2",
    "array": [
        {
            "PairString": "1",
            "PairInt": 1
        },
        {
            "PairString": "2",
            "PairInt": 2
        }
    ]
}

This is implemented with a Delphi class like:

  TMyItem = class
  public
    PairString: string;
    PairInt: Integer;
  end;
  
  TExample = class
  public
    pair1: string;
    pair1: string;
    array: TArray<TMyItem>; 
  end;

How can I create a similar model for a dynamic set of pairs? Is it possible in that way?

For example, a JSON like:

{
    "dictionary": {
        "key1": "KeyValue1",
        "key2": "KeyValue2",
        "key3": "KeyValue3",
        .....
        "keyN": "KeyValueN"
    }
}

In C#, that is implemented with the use of a Dictionary. In Delphi, TDictionary or TObjectDictionary when serialized, extracts all the members of the class and produces a different and invalid structure.

I have tried to use REST.JsonReflect and create a custom JsonReflectAttribute and TJSONInterceptor and implement ObjectConverter in order to edit the final TJSONObject and add the pairs dynamically.

The following code intercepts and converts the dictionary to TJSONObject, which also extracts an invalid structure, because it is serialized as a class.

uses System.Rtti, Rest.Json, System.Generics.Collections, Rest.JsonReflect;

type
  TDictionaryInterceptor = class(TJSONInterceptor)
  public
    function ObjectConverter(Data: TObject; Field: string): TObject; override;
  end;
  
  function TDictionaryInterceptor.ObjectConverter(Data: TObject; Field: string): TObject;
    function DictionaryToJsonObject(ADictionary: TDictionary<string, string>): TJSONObject;
    var
        JsonObject: TJSONObject;
        KeyValuePair: TPair<string, string>;
    begin
        JsonObject := TJSONObject.Create;
        try
            for KeyValuePair in ADictionary do
            begin
              JsonObject.AddPair(TJSONPair.Create(TJSONString.Create(TValue.From<string>(KeyValuePair.Key).ToString),
                                                  TJSONString.Create(TValue.From<string>(KeyValuePair.Value).ToString))
              );
            end;
            Result := JsonObject;
        finally
        end;
    end;
  var
    ctx: TRttiContext;
    Dict: TObjectDictionary<string, string>;
    RttiProperty: TRttiProperty;
  begin
    RttiProperty := ctx.GetType(Data.ClassInfo).GetProperty(Copy(Field, 2, MAXINT));
    Dict := TObjectDictionary<string, string>(RttiProperty.GetValue(Data).AsObject);

    Result := DictionaryToJsonObject(Dict);
  end;
      
  DictionaryReflectAttribute = class(JsonReflectAttribute)
  public
    constructor Create;
  end;
    
  constructor DictionaryReflectAttribute.Create;
  begin
    inherited Create(ctObject, rtObject, TDictionaryInterceptor);
  end;
  
  TExample = class
  public
    [DictionaryReflectAttribute]
    dictionary: TObjectDictionary<string, string>;
  end;

Is this the correct direction, or is there an alternative way to serialize TDictionary in the form of the example?

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
stmpakir
  • 319
  • 2
  • 10
  • 1
    https://github.com/synopse/mORMot2/blob/master/src/core/mormot.core.collections.pas features direct JSON serialization of its `IKeyValue` dictionary (and also binary serialization and a lot of other features). It is more likely to use records and arrays for its nested data, rather than classes, but it is something worth considering. Or you can use a `TDocVariant` storage from https://github.com/synopse/mORMot2/blob/master/src/core/mormot.core.variants.pas which has a lot of methods to process any kind of unfixed JSON and search/filter within, with no class/record involved. – Arnaud Bouchez Apr 20 '23 at 08:04
  • @mORMauthor thank you for your info. I will lookout for an example. I was hoping that i could use delphi's native library, and avoid to learn a new framework due to lack of time. I use classes as equivalent to C#, in order to avoid structuring manually a TJSONObject. Building a TJSONObject is actually my workaround in situation with dynamic JSON, but it is not elegant, and i look for a better solution. – stmpakir Apr 20 '23 at 15:03

0 Answers0