Skip to content

NeonFactory & NeonItemFactory

Paolo Rossi edited this page Mar 25, 2024 · 2 revisions

The NeonItemFactory attribute

Tip

${\textsf{\color{darkgreen}What problem does it solves}}$: When you have list of different types based on the JSON you read, the NeonItemFactory attribute lets you create different type of objects (without breaking the type compatibility of Delphi)

The NeonItemFactory attribute is used to (custom) create the items of a collection (array, list, dictionary, etc…), this gives Neon the feature to support heterogeneous collections.

Let’s say you have this classes: TBaseClass, TDerivedClass1, TDerivedClass2, TDerivedClass3. Now using the NeonItemFactory attribute if you declare a property of type TBaseClass you can create items of type TDerivedClass1, TDerivedClass2, TDerivedClass3. Of course you have to deduce the object type based on the JSON you read from.

So, how does it work?

In order to create an object factory you must derive from a base (and very simple) class: TCustomFactory and override the virtual abstract method Build:

/// <summary>
///   Base class for an Object Factory
/// </summary>
TCustomFactory = class abstract(TObject)
public
  function Build(const AType: TRttiType; AValue: TJSONValue): TObject; virtual; abstract;
end;

As you can see you have to provide an object as a result but you can decide based on the AType parameter and the JSON Neon is reading from (for example):

[
  {
    "$type": "TDerivedClass1",
    "Name": "Obj1",
    "Prop1": 123
  },
  {
    "$type": "TDerivedClass3",
    "Name": "Obj1",
    "Prop1": "This is a sample value",
    "Prop2": true
  },
  {
    "$type": "TDerivedClass2",
    "Name": "Obj1",
    "Prop1": [3,5,7,9],
    "PropX": "Another sample value"
  }
]

Of course the $type is only in this JSON example, you can have any JSON fragment as a source of your objects as long as you can infer the type of the object to be created from the same JSON fragment! Let see ho to use the NeonItemFactory attribute:

type
  TBaseClass = class
    // Base class stuff
  end;

  TDerivedClass1 = class(TBaseClass)
    // DerivedClass1 class stuff
  end;

  TDerivedClass2 = class(TBaseClass)
    // DerivedClass2 class stuff
  end;

  TDerivedClass3 = class(TBaseClass)
    // DerivedClass3 class stuff
  end;

  TMyClass = class
  private
    FName: string;
    FList: TArray<TBaseClass>;
  public
    constructor Create;
    destructor Destroy; override;
  public
    property Name: string read FName write FName;

    [NeonItemFactory(TBaseClassFactory)]
    property List: TArray<TBaseClass> read FList write FList;
  end;

The NeonFactory attribute

Tip

${\textsf{\color{darkgreen}What problem does it solves}}$: When you have to create a sub-object of a class based on the JSON, the NeonFactory attribute lets you create different type of objects (without breaking the type compatibility of Delphi)

The NeonFactory attribute is quite similar to the previous attributeNeonItemFactory but is used to signal Neon to create a sub-object of a class. Let see an example

type
  TLink = class
  private
    FLinkType: string;
    FAddress: string;
  public
    property LinkType: string read FLinkType write FLinkType;
    property Address: string read FAddress write FAddress;
  end;

  TContainer = class
  private
    FLink: TLink;
    FName: string;
  public
    property Name: string read FName write FName;
    property Link: TLink read FLink write FLink;
  end;

var
  container: TContainer;

As you can see the TContainerclass has a sub-object but it hasn’t a constructor so the Link properties will be nil after you create the object container.

type
  ...

  TContainer = class
  private
    FLink: TLink;
    FName: string;
  public
    property Name: string read FName write FName;
    [NeonFactory(TLinkFactory)]
    property Link: TLink read FLink write FLink;
  end;

Now if you annotate the Link property with the NeonFactory attribute Neon will create the object invoking the TLinkFactory factory.

Warning

You can create new object through the NeonFactory attribute only if the property/field is nil

Clone this wiki locally