How to avoid parsing of json object in ISuperObject-typed field

174 Views Asked by At

Long time ago we decided to use XSuperObject library for marshalling objects into json for transition between two parts of system. One of fields represents as ISuperObject on server side and as various object on client side.

The idea is that I send objects to server and then it returns one of this back without any changes. For example I send this:

[
  {
    "NAME": "Today",
    "VALUE": {"loginIndex": 0,"shipmentDate": "2023-09-08","shipmentAddressId": "","paymentMethodId": "","shipmentMethodId": "","shipmentOfficeId": ""},
    "IS_DEFAULT": false,
    "ID": 0,
    "WH_ID": 0
  },
  {
    "NAME": "Monday",
    "VALUE": {"loginIndex": 0,"shipmentDate": "2023-09-11","shipmentAddressId": "","paymentMethodId": "","shipmentMethodId": "","shipmentOfficeId": ""},
    "IS_DEFAULT": false,
    "ID": 0,
    "WH_ID": 0
  },
  {
    "NAME": "Tuesday",
    "VALUE": {"loginIndex": 0,"shipmentDate": "2023-09-12","shipmentAddressId": "","paymentMethodId": "","shipmentMethodId": "","shipmentOfficeId": ""},
    "IS_DEFAULT": false,
    "ID": 0,
    "WH_ID": 0
  }
]

and translate into object with this structure:

TMySelectedItem = record
  NAME:String;
  VALUE:ISuperObject;
  IS_DEFAULT:Boolean;
  ID:Integer;
  WH_ID:Integer;
end

When I translate JSON to object using TSuperRecord<TMySelectedItem>.FromJSON(Resp.Data[i]), extract VALUE field and translate it into string using VALUE.AsJSON, I expect this:

{"loginIndex": 0,"shipmentDate": "2023-09-08","shipmentAddressId": "","paymentMethodId": "","shipmentMethodId": "","shipmentOfficeId": ""}

but instead, server returns me this:

{"loginIndex": 0,"shipmentDate": "2023-09-07Z","shipmentAddressId": "","paymentMethodId": "","shipmentMethodId": "","shipmentOfficeId": ""}

As you can see, it not only recognize field shipmentDate as date, as I suppose, it also changes timezone despite lack of time value in this string and especially I didn't tell it, that this field is a date. Can I mark field, typed ISuperObject to translate into output json as is, without any changes?

1

There are 1 best solutions below

0
On BEST ANSWER

The library you use (XSuperObject) has built-in recognition of date, time and date-time values from JSON string values when reading JSON. This is enabled by default.

Some of the APIs in the library gives you control over this behavior - TSuperObject.Create(JSON: String = '{}'; const CheckDate: Boolean = True), but this is not the case of TSuperRecord<T>.FromJson(const JSON: String).

In such a case you could use the version of FromJson method that takes ISuperObject as an argument that has date recognition explicitly disabled:

var LSuperObject: ISuperObject := TSuperObject.Create(Resp.Data[i], {CheckDate}False);
var Item := TSuperRecord<TMySelectedItem>.FromJSON(LSuperObject);

But this won't work either due to bugs in the library:

  1. The value of CheckDate parameter is not propagated to nested objects. Method TBaseJSON<T, Typ>.GetObject(V: Typ): ISuperObject returns new instance of TSuperObject with CheckDate enabled by default.
  2. Unmarshalling values of type ISuperObject inside TSuperRecord<T>.FromJSON() also ignores the value of CheckDate parameter that was used to create TSuperObject instance. See method TSuperObject.Clone: ISuperObject

Those are just two bugs that apply to your scenario, but I guess there's more, because the library lacks test suite. In theory you could ask the author to fix those bugs, or contribute bug fixes, but based on issues and commits the project seems pretty much dead to me.

The library contains static class TJSONDateManager that is responsible for recognizing dates. It contains Formats class property which is initialized with ISO-8601 format. Clearing these formats will disable any date recognition and the solution to your specific problem will become one-liner:

TJSONDateManager.Formats.Clear;

At this point you depend on the library that is buggy and you probably won't get any support. I strongly recommend you to switch to some other library.

FWIW, the proposed fixes to bugs mentioned above are:

function TBaseJSON<T, Typ>.GetObject(V: Typ): ISuperObject;
begin
  Result := Nil;
  if not Member(V) then
    Member<TJSONObject, Pointer>(V, Nil);

  //Original: Result := TSuperObject.Create(GetValue<TJSONObject>(V));
  Result := TSuperObject.Create(GetValue<TJSONObject>(V), FCheckDate);
end;

function TSuperObject.Clone: ISuperObject;
begin
  //Original: Result := SO(AsJSON);
  Result := TSuperObject.Create(AsJSON, FCheckDate);
end;