Simple JSON deserialization of records incorrect (Delphi Sydney [10.4.1])
To solve the problem temporarily, I have the following quick solution for you:
- Make a copy of the standard library
Data.DBXJSONReflect
and name it e.g.Data.TempFix.DBXJSONReflect
. - Change all includes/uses in your project accordingly.
After that navigate in Data.TempFix.DBXJSONReflect
to line 2993:
jsonFieldVal := TJSONArray(JsonValue).Items[I];
And replace it with the following code:
try jsonFieldVal := TJSONArray(JsonValue).Items[I];except on e:Exception do if e is EArgumentOutOfRangeException then continue else raise;end;
After that the whole method should look like this:
function TJSONUnMarshal.JSONToTValue(JsonValue: TJSONValue; rttiType: TRttiType): TValue;var tvArray: array of TValue; Value: string; I: Integer; elementType: TRttiType; Data: TValue; recField: TRTTIField; attrRev: TJSONInterceptor; jsonFieldVal: TJSONValue; ClassType: TClass; Instance: Pointer;begin // null or nil returns empty if (JsonValue = nil) or (JsonValue is TJSONNull) then Exit(TValue.Empty); // for each JSON value type if JsonValue is TJSONNumber then // get data "as is" Value := TJSONNumber(JsonValue).ToString else if JsonValue is TJSONString then Value := TJSONString(JsonValue).Value else if JsonValue is TJSONTrue then Exit(True) else if JsonValue is TJSONFalse then Exit(False) else if JsonValue is TJSONObject then // object... Exit(CreateObject(TJSONObject(JsonValue))) else begin case rttiType.TypeKind of TTypeKind.tkDynArray, TTypeKind.tkArray: begin // array SetLength(tvArray, TJSONArray(JsonValue).Count); if rttiType is TRttiArrayType then elementType := TRttiArrayType(rttiType).elementType else elementType := TRttiDynamicArrayType(rttiType).elementType; for I := 0 to Length(tvArray) - 1 do tvArray[I] := JSONToTValue(TJSONArray(JsonValue).Items[I], elementType); Exit(TValue.FromArray(rttiType.Handle, tvArray)); end; TTypeKind.tkRecord, TTypeKind.tkMRecord: begin TValue.Make(nil, rttiType.Handle, Data); // match the fields with the array elements I := 0; for recField in rttiType.GetFields do begin Instance := Data.GetReferenceToRawData; try jsonFieldVal := TJSONArray(JsonValue).Items[I]; except on e:Exception do if e is EArgumentOutOfRangeException then continue else raise; end; // check for type reverter ClassType := nil; if recField.FieldType.IsInstance then ClassType := recField.FieldType.AsInstance.MetaclassType; if (ClassType <> nil) then begin if HasReverter(ClassType, FIELD_ANY) then RevertType(recField, Instance, Reverter(ClassType, FIELD_ANY), jsonFieldVal) else begin attrRev := FieldTypeReverter(recField.FieldType); if attrRev = nil then attrRev := FieldReverter(recField); if attrRev <> nil then try RevertType(recField, Instance, attrRev, jsonFieldVal) finally attrRev.Free end else recField.SetValue(Instance, JSONToTValue(jsonFieldVal, recField.FieldType)); end end else recField.SetValue(Instance, JSONToTValue(jsonFieldVal, recField.FieldType)); Inc(I); end; Exit(Data); end; end; end; // transform value string into TValue based on type info Exit(StringToTValue(Value, rttiType.Handle));end;