delphimvcframework icon indicating copy to clipboard operation
delphimvcframework copied to clipboard

add a nested json object to the dataset

Open Ravaut123 opened this issue 5 years ago • 3 comments

I've change some code in TMVCJsonDataObjectsSerializer.JsonObjectToDataSet to have the values of a nested json object into the database. (see https://www.facebook.com/groups/delphimvcframework/permalink/2593069444109512/)

Is it possible to add the code into the repositorie?

procedure TMVCJsonDataObjectsSerializer.JsonObjectToDataSet(const AJsonObject: TJDOJsonObject; const ADataSet: TDataSet;
  const AIgnoredFields: TMVCIgnoredList; const ANameCase: TMVCNameCase);
var
  Field: TField;

  sFieldName: string;
  sSubName: string;
  Name: string;

  SS: TStringStream;
  SM: TMemoryStream;
  NestedDataSet: TDataSet;


  procedure ParseFieldName(const aFieldName: string; out aTableName: String; out aColumnName: string);
  var
    i: Integer;
  begin
    i := Pos('.', aFieldName);
    if i > 1 then
    begin
      aTableName := Copy(aFieldName, 1, i - 1);
      aColumnName := Copy(aFieldName, i + 1);
    end
    else
    begin
      aColumnName := '';
      aTableName := aFieldName;
    end;
  end;

begin
  if (ADataSet.State in [dsInsert, dsEdit]) then
  begin
    for Field in ADataSet.Fields do
    begin
      name := GetNameAs(ADataSet.Owner, Field.Name, Field.FieldName);

      if (IsIgnoredAttribute(AIgnoredFields, name)) or (IsIgnoredComponent(ADataSet.Owner, Field.Name)) then
        continue;

      case GetNameCase(ADataSet, ANameCase) of
        ncLowerCase:
          name := LowerCase(Field.FieldName);
        ncUpperCase:
          name := UpperCase(Field.FieldName);
      end;

      sFieldName := Name;
      ParseFieldName(sFieldName, Name, sSubName);

      if not AJsonObject.Contains(name) then
        continue;

      if (AJsonObject[name].Typ = jdtObject) and (AJsonObject.Values[name].ObjectValue = nil) then
      // Nullable Type
      begin
        Field.Clear;
        continue;
      end;

      case Field.DataType of
        TFieldType.ftBoolean:
          Field.AsBoolean := AJsonObject.B[name];

        TFieldType.ftInteger, TFieldType.ftSmallint, TFieldType.ftShortint, TFieldType.ftByte, TFieldType.ftLongWord, TFieldType.ftWord, TFieldType.ftAutoInc:
          Field.AsInteger := AJsonObject.I[name];

        TFieldType.ftLargeint:
          Field.AsLargeInt := AJsonObject.L[name];

        TFieldType.ftCurrency:
          Field.AsCurrency := AJsonObject.F[name];

        TFieldType.ftSingle:
          Field.AsSingle := AJsonObject.F[name];

        TFieldType.ftFloat, TFieldType.ftFMTBcd, TFieldType.ftBCD:
          Field.AsFloat := AJsonObject.F[name];

        ftString, ftWideString, ftMemo, ftWideMemo:
        begin
          if (AJsonObject[name].Typ = jdtObject) then
          begin
            if sSubName.IsEmpty then
            begin
              Field.AsWideString := AJsonObject.O[name].ToJSON;
            end
            else
            begin
              begin
                Field.AsWideString := AJsonObject.O[name].S[sSubName];
              end;
            end;
          end
          else
          begin
            Field.AsWideString := AJsonObject.S[name];
          end;
        end;

        TFieldType.ftDate:
          Field.AsDateTime := ISODateToDate(AJsonObject.S[name]);

        TFieldType.ftDateTime:
          Field.AsDateTime := ISOTimeStampToDateTime(AJsonObject.S[name]);

        TFieldType.ftTimeStamp, TFieldType.ftTime:
          Field.AsDateTime := ISOTimeToTime(AJsonObject.S[name]);

{$IFDEF TOKYOORBETTER}
        TFieldType.ftGuid:
          Field.AsGuid := StringToGUID(AJsonObject.S[name]);
{$ENDIF}
        TFieldType.ftGraphic, TFieldType.ftBlob, TFieldType.ftStream:
          begin
            SS := TStringStream.Create(AJsonObject.S[name]);
            try
              SS.Position := 0;
              SM := TMemoryStream.Create;
              try
                TMVCSerializerHelper.DecodeStream(SS, SM);
                TBlobField(Field).LoadFromStream(SM);
              finally
                SM.Free;
              end;
            finally
              SS.Free;
            end;
          end;

        TFieldType.ftDataSet:
          begin
            NestedDataSet := TDataSetField(Field).NestedDataSet;

            NestedDataSet.First;
            while not NestedDataSet.Eof do
              NestedDataSet.Delete;

            case GetDataType(ADataSet.Owner, Field.Name, dtArray) of
              dtArray:
                begin
                  JsonArrayToDataSet(AJsonObject.A[name], NestedDataSet, AIgnoredFields, ANameCase);
                end;
              dtObject:
                begin
                  NestedDataSet.Edit;
                  JsonObjectToDataSet(AJsonObject.O[name], NestedDataSet, AIgnoredFields, ANameCase);
                  NestedDataSet.Post;
                end;
            end;
          end;
      else
        raise EMVCDeserializationException.CreateFmt('Cannot find type for field "%s"', [Field.FieldName]);
      end;
    end;
  end;
end;

Ravaut123 avatar Dec 10 '19 15:12 Ravaut123

Thanks for your contribute. However, we can merge this code only if the behaviour is simmetric with the serialization side. Can you provide an updated test project to show the serialization and the deserialization process?

danieleteti avatar Feb 10 '20 22:02 danieleteti

Hi, see more in https://github.com/viniciussanchez/dataset-serialize It's perfect

viniciussanchez avatar Feb 11 '20 00:02 viniciussanchez

@viniciussanchez the project uses system.json which is quite slow compared to jsondataobjects (used by dmvcframework). Having a subproject for dataset serialization is ok (if adds something to the current implementation), but it should be simple to integrate and must use jsondataobjects. Thank you for suggesting

danieleteti avatar Feb 11 '20 08:02 danieleteti