-
Notifications
You must be signed in to change notification settings - Fork 97
NeonInclude
The NeonInclude attribute helps you to define fields inclusion rules. To fully understand it let’s have a look at the attribute’s definition in the Neon.Core.Attributes.pas unit:
/// <summary>
/// The IncludeIf enum values define when to include the field or property.
/// </summary>
IncludeIf = (
/// <summary>
/// Include the member if it's not nil
/// </summary>
NotNull,
/// <summary>
/// Include the member if the value it's not empty
/// </summary>
NotEmpty,
/// <summary>
/// Include the member if it's value it's not the default value
/// </summary>
NotDefault,
/// <summary>
/// Include the member always
/// </summary>
Always,
/// <summary>
/// Include the member based on the result of the function specified as string
/// (default function is ShouldInclude)
/// </summary>
CustomFunction);
TIncludeValue = record
Present: Boolean;
Value: IncludeIf;
IncludeFunction: string;
end;
/// <summary>
/// The Neon annotation NeonInclude tells Neon to include the property (or field)
/// based on the value of the enumeration Include
/// </summary>
NeonIncludeAttribute = class(TCustomAttribute)
private
FIncludeValue: TIncludeValue;
public
constructor Create(AIncludeValue: IncludeIf = IncludeIf.Always; const AIncludeFunction: string = 'ShouldInclude');
property IncludeValue: TIncludeValue read FIncludeValue write FIncludeValue;
end;As you can see the attribute can have 2 parameters: the first parameter controls when to include the field or property:
-
NotNullTells Neon to include the value only if it’s a nonnilvalue. It’s useful for objects and nullable values. -
NotEmptyTells Neon to include the value only if it’s not Empty. Useful on arrays, lists, generic collections, objects, records, strings, etc… -
NotDefaultTells Neon to include the value only if it has a non default value, for example 0 for an integer field. -
AlwaysTells Neon to always include the value. It’s the default setting. -
CustomFunctionTells Neon to include the value based on the result (True or False) of a custom function.
The second parameter lets you specify the name of the custom function and it has meaning only if the first parameter is CustomFunction
The simplest way to use the NeonInclude attribute is to use it without additional parameters or specifying IncludeIf.Always as value for the first parameter . Let’s say you have a private field that you want to include in the resulting JSON:
TPerson = record
private
InternalField1: Integer;
InternalField2: Integer;
[NeonInclude]
InternalField3: Integer;
public
Name: string;
Age: Integer;
end;If you serialize the struct the resulting JSON will be:
{
"InternalField3": 55,
"Name": "Paolo",
"Age": 50
}This is the most simple use of the NeonInclude attribute, now if you want more flexibility you must start using the parameter IncludeIf.
If you don’t want to serialize object reference that are not assigned, you have to use IncludeIf.NotNull as a first parameter. This setting applies to object references and nullable values.
TPerson = class
private
FAddresses: TStringList;
FAge: Integer;
FName: string;
public
property Age: Integer read FAge write FAge;
property Name: string read FName write FName;
[NeonInclude(IncludeIf.NotNull)]
property Addresses: TStringList read FAddresses write FAddresses;
end;In this example if (for some reason) the field Addresses is nil the field doesn’t get serialized.
Well, let’s assume you have a list or an array of elements and you don’t want to serialize that list if the list doesn’t have any items, or perhaps you have an object and sometimes because of serialization’s rules you have no field to serialize resulting in an empty JSON object. To avoid all that you can use the IncludeIf.NotEmpty as a first parameter.
TCommand = record
public
Name: string;
Parameters: string;
[NeonInclude(IncludeIf.NotEmpty)]
Packet: TArray<Integer>;
end;The IncludeIf.NotEmpty value is effective with: arrays, lists, generic lists, object, records, strings, dates.
There is a bit of an overlap on the meaning of NotDefault and NotEmpty. The NotDefault value applies also on Integer, Int64 and Float, so when the value is 0 the field doesn’t get serialized.
The NotDefault applies also to arrays, lists, generic lists, object, records, strings, dates.
TCommand = record
public
Name: string;
Parameters: string;
[NeonInclude(IncludeIf.NotDefault)]
Value: Integer;
end;In this example the resulting JSON will not have the Value field if the content of the field is 0
The previous parameters of the NeonInclude attribute deal with “fixed” condition such as nil values, empty values, default values; instead if you want to include/ignore a field based on custom conditions (other fields value, a state in your application, anything…) you have to provide some code to do that!
The last value of the enum IncludeIf is CustomFunction and that means that you have to provide the function name to be called in the next parameter of the attribute:
TPerson = class
private
FName: string;
FAge: Integer;
FTitle: string;
function ShouldInclude(const AContext: TNeonIgnoreIfContext): Boolean;
public
property Age: Integer read FAge write FAge;
property Name: string read FName write FName;
[NeonInclude(IncludeIf.CustomFunction, 'ShouldInclude')]
property Title: string read FTitle write FTitle;
end;Here is an example of the ShouldInclude function
function TPerson.ShouldInclude(const AContext: TNeonIgnoreIfContext): Boolean;
begin
Result := False;
// You can filter by the member name
if SameText(AContext.MemberName, 'Title') then
begin
// And you can filter on additional conditions
if Age > 18 then
Result := True;
end
// You can reuse (only if you want) the same function for several members
else if SameText(AContext.MemberName, 'Name') then
begin
Result := True;
end;
end;As you can see the function to be called has a determined signature:
function ShouldInclude(const AContext: TNeonIgnoreIfContext): Boolean
The function can address more than one field, you get the member’s name in the AContext parameter, along with the operation type (Serialize, Deserialize). The ShouldInclude name is the default so you can write:
function ShouldInclude(const AContext: TNeonIgnoreIfContext): Boolean;
public
[NeonInclude(IncludeIf.CustomFunction)]
property Title: string read FTitle write FTitle;
end;Neon Libray • Project Home • paolo's Repositories