Search code examples
jsondelphideserializationabstract-class

Delphi deserialize JSON with abstract class


I want to serialize and deserialize an object with the built-in functions TJson.JsonToObject<T> and TJson.ObjectToJsonObject. The object contains some nested objects and one of these is of an abstract type. Is there any chance, to tell the deserializer, which concrete object to create? Can I maybe use a custom JSONInterceptor for this nested object?

The classes are defined as following:

type
  TAngPos = class (TObject)
    strict private
      var
        FPrimkey: Integer;
        FAng_ID: Integer;
        FPosNr: Integer;
        FArt_ID: Integer;
      
        FPositionstyp: TPositionstyp; // <--- abstract
  end;
type
  TPositionstyp = class abstract (TObject)
    strict protected
      var
        FArtikel: TArtikel;
  end;
type
  TPositionstypArtikel = class (TPositionstyp);
type
  TPositionstypAngPosKonf = class (TPositionstyp)
    strict private
      var
        FGrundeinheit: TAngPos;
        FEinbaukomponenten: TObjectList<TAngPos>;
  end;
type
  TArtikel = class (TObject)
    strict private
      var
        FPrimkey: Integer;
        FStatus: Integer;
        FTyp: Integer;
        FBeschreibung: string;
        FHerstellerNr: string;
  end;

The corresponding JSON looks like this:

for TPositionstypArtikel:

{
  "primkey": 23930,
  "ang_ID": 2400,
  "posNr": 40,
  "art_ID": 46210,
  "positionstyp": { // PositionstypArtikel
    "artikel": {
      "primkey": 46210,
      "status": 1,
      "typ": 2,
      "beschreibung": "MyDescription",
      "herstellerNr": "MyVendorNr"
    }
  }
}

and for TPositionstypAngPosKonf

{
    "primkey": 2,
    "ang_ID": 1,
    "posNr": 10,
    "art_ID": 44041,
    "positionstyp": { // TPositionstypAngPosKonf
      "grundeinheit": { // <-- TAngPos
        "primkey": 33067,
        "ang_ID": 0,
        "posNr": 20,
        "art_ID": 44092,
        "positionstyp": {
          "artikel": {
            "primkey": 44092,
            "status": 2,
            "typ": 4,
            "beschreibung": "MyDescriptionGrundeinheit",
            "herstellerNr": "MyVendorNrGrundeinheit"
          }
        }
      },
      "einbaukomponenten": { // <-- TObjectList<TAngPos>
        "ownsObjects": true,
        "listHelper": [
          {
            "primkey": 33068,
            "ang_ID": 0,
            "posNr": 30,
            "art_ID": 44399,
            "positionstyp": {
              "artikel": {
                "primkey": 44399,
                "status": 2,
                "typ": 4,
                "beschreibung": "MyDescriptionEinbaukomponente1",
                "herstellerNr": "MyVendorNrEinbaukomponente1"
              }
            }
          },
          {
            "primkey": 33069,
            "ang_ID": 0,
            "posNr": 40,
            "art_ID": 44398,
            "positionstyp": {
              "artikel": {
                "primkey": 44398,
                "status": 2,
                "typ": 4,
                "beschreibung": "MyDescriptionEinbaukomponente2",
                "herstellerNr": "MyVendorNrEinbaukomponente2"
              }
            }
          }
        ]
      },
      "artikel": {
        "primkey": 44041,
        "status": 1,
        "typ": 3,
        "beschreibung": "MyDescriptionKonfKopf",
        "herstellerNr": ""
      }
    }

After deserialization of the object, I can check, if the nested object is of a specific type with the is-operator, but unfortunately it's neither TPositionstypArtikel nor TPositionstypAngPosKonf.


Solution

  • Thanks for the comments. I ended up in changing the structure of the class. It doesn't contain the abstract type anymore, but rather a separate variable of type TPositionstypAngPosKonf, which is either nil or not. The TArtikel from the TPositionstyp now inhabits directly in the TAngPos and the two classes TPositionstyp and TPositionstypArtikel don't exist anymore. So it basically looks like this

    type
      TAngPos = class (TObject)
        strict private
          var
            FPrimkey: Integer;
            FAng_ID: Integer;
            FPosNr: Integer;
            FArt_ID: Integer;
          
            FArtikel: TArtikel;
            FPositionstypAngPosKonf: TPositionstypAngPosKonf; // may be nil
      end;
    
    type
      TPositionstypAngPosKonf = class (TObject)
        strict private
          var
            FGrundeinheit: TAngPos;
            FEinbaukomponenten: TObjectList<TAngPos>;
      end;
    
    type
      TArtikel = class (TObject)
        strict private
          var
            FPrimkey: Integer;
            FStatus: Integer;
            FTyp: Integer;
            FBeschreibung: string;
            FHerstellerNr: string;
      end;