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?
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:
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.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;