Neon JSON Serialization for Delphi
repository·master·Indexed 43 days ago
https://github.com/paolo-rossi/delphi-neonA high-performance JSON serialization library for Delphi designed for RESTful data exchange and serving as the default engine for the WiRL REST Library. It supports a wide range of Delphi types, including classes and records, and includes the Classify CLI tool for automatically generating Delphi entities from JSON documents.
What's inside Neon
- Neon is a serialization library for Delphi designed to convert Delphi objects, records, and simple types to and from JSON. It is optimized for RESTful communication, ensuring that the exchanged data is pure JSON without extra metadata or injected fields. Neon is the default serialization engine used by the WiRL REST Library.
Handle read-only and write-only members
masterNeon determines serialization and deserialization capability based on the RTTI
readandwriteclauses of a property.Serialization (Writing JSON)
By default, read-only properties are included in the JSON. To exclude them, use:
LConfig := TNeonConfiguration.Default.SetIgnoreReadOnlyProps(True);Exemption: A read-only property of a class or interface type is always kept in the JSON to ensure composite entities (like collections) are preserved.
Deserialization (Reading JSON)
Deserialization is governed by whether a member is writable. A read-only property (one without a
writeclause or a setter) will be skipped during deserialization.Overriding Behavior
If you need to change how a member is treated, use these attributes:
[NeonInclude(IncludeIf.Always)]: Forces serialization even ifIgnoreReadOnlyPropsis enabled. Note: This does not create a setter; the property will still be skipped during reading.[NeonIgnore]: Explicitly drops the member from both reading and writing.[NeonSetter('MethodName')]: Tells Neon to use a specific method to write the value, making the property effectively writable for deserialization.
type TEntity = class private FVersion: string; public procedure SetVersionValue(const AValue: string); [NeonSetter('SetVersionValue')] property Version: string read FVersion; end;Register custom serializers for Delphi types
masterNeon supports most standard Delphi types, but specialized types like
TGUID,TBytes,TCollection, orTDataSetrequire explicit registration of their serializers in the configuration.Important:
TNeonConfiguration.Defaultstarts with an empty registry. If you do not register a serializer, Neon falls back to generic RTTI handling, which often produces incorrect results for these types.To register serializers, use the
RegisterSerializermethod on your configuration object. It is recommended to use theINeonConfiguration.RegisterSerializerpath to ensure the serializer'sChangeConfighook is executed.Example registration:
uses Neon.Core.Serializers.RTL, Neon.Core.Serializers.DB, Neon.Core.Serializers.VCL, Neon.Core.Serializers.Nullables; LConfig := TNeonConfiguration.Default .RegisterSerializer(TGUIDSerializer) .RegisterSerializer(TBytesSerializer) .RegisterSerializer(TStreamSerializer) .RegisterSerializer(TJSONValueSerializer) .RegisterSerializer(TCollectionSerializer) .RegisterSerializer(TDataSetSerializer) .RegisterSerializer(TImageSerializer); RegisterNullableSerializers(LConfig.GetSerializers);LConfig := TNeonConfiguration.Default .RegisterSerializer(TGUIDSerializer) .RegisterSerializer(TBytesSerializer) .RegisterSerializer(TStreamSerializer) .RegisterSerializer(TJSONValueSerializer) .RegisterSerializer(TCollectionSerializer) .RegisterSerializer(TDataSetSerializer) .RegisterSerializer(TImageSerializer); RegisterNullableSerializers(LConfig.GetSerializers);Profile Neon performance with TNeonLogger
masterYou can profile the performance of the Neon engine using
TNeonLogger. It tracks elapsed time and call counts for various internal sections (e.g.,Serialize:Object,Core:GetNeonMembers).Profiling is disabled by default to minimize overhead. When enabled, timings are inclusive of nested calls.
TNeonLogger.ProfileReset; TNeonLogger.ProfileEnabled := True; try LJSON := TNeon.ObjectToJSON(AObject, LConfig); LJSON.Free; finally TNeonLogger.ProfileEnabled := False; end; WriteLn(TNeonLogger.ProfileReport); // Outputs a report with Section | Calls | Total (ms) | Avg (us) | %TNeonLogger.ProfileReset; TNeonLogger.ProfileEnabled := True; try LJSON := TNeon.ObjectToJSON(AObject, LConfig); LJSON.Free; finally TNeonLogger.ProfileEnabled := False; end; WriteLn(TNeonLogger.ProfileReport);Use the Classify CLI to generate Delphi entities from JSON
masterThe
Classifycommand-line tool is a front-end forTNeonEntityGenerator. It reads a JSON document and automatically writes the corresponding Delphi classes or records required to hold that data.Basic Usage:
Classify [options] <document.json>If you run
Classifywith no arguments, it provides a guided tour of the generator's capabilities. If you provide a JSON file but no options, it prints the generated Delphi code to standard output... \.. extbackslash Bin\Classify ..\.. extbackslash Data\customer.jsonHandle JSON documents with an array root
masterWhen the JSON document's root is an array (e.g.,
[{}, {}]), the items in the array are treated as the primary entity. The root name (set via-r) becomes the name of the items, and the container itself is aliased as<prefix><name>List(e.g.,TOrderList).Example command:
Classify -r Order -u Api.Orders --no-header ..\..\Data\orders.json.. \.. extbackslash Bin\Classify -r Order -u Api.Orders --no-header ..\.. extbackslash Data\orders.jsonInstall Neon in your Delphi project
masterTo install Neon, simply add the
Sourcedirectory from the repository to your Delphi project search path. The library has no external dependencies, but requires the following Delphi units:System.JSON(Delphi XE6+)System.Rtti(Delphi 2010+)System.Generics.Collections(Delphi 2009+)
Tested compatibility includes Delphi 12 Athens, 11 Alexandria, 10.4 Sydney, 10.3 Rio, and 10.2 Tokyo. It should compile on Delphi XE7 and higher with minimal effort.
# Add the 'Source' folder to your Delphi Project PathExplore Neon via Demos
masterThe repository includes several demonstration projects to help you understand the library's capabilities:
- Neon Main Demo: Demonstrates serialization/deserialization of simple types, records, classes, and Delphi-specific types like
TStringListandTDataSet. - Neon Benchmarks Demo: Compares Neon's performance against other serialization engines.
- Console Demos: A collection of command-line tools for advanced usage:
BenchmarksConsole: Compares Neon againstREST.JsonandSystem.JSON.Serializers(TJsonSerializer).ProfilingConsole: UsesTNeonLoggerto profile internal stages like RTTI resolution and member preparation.Classify: Converts JSON documents into Delphi entities.SchemaConsole: Generates JSON Schema Draft 2020-12 from Delphi types and validates JSON against them.ReadOnlyConsole: Demonstrates handling of read-only and write-only members.
- Neon Main Demo: Demonstrates serialization/deserialization of simple types, records, classes, and Delphi-specific types like
Configure Neon serialization via INeonConfiguration
masterNeon provides extensive configuration through the
INeonConfigurationinterface. You can customize how Delphi members are mapped to JSON keys, how visibility is handled, and how specific types are processed.Key configuration options include:
- Word Case: Set the JSON key casing using options like
Unchanged,UPPERCASE,lowercase,PascalCase,camelCase,snake_case,kebab-case, orSCREAMING_SNAKE_CASE. - Member Types: Choose whether to serialize
Fields,Properties, or both. - Member Visibility: Filter by
private,protected,public, orpublished. - Ignore Field Prefix: An option to automatically strip the
Fprefix from private/protected fields (e.g.,FFirstNamebecomesFirstName). Warning:TNeonConfiguration.Snakeand.ScreamingSnaketurn this on by default. - Read-only Properties: Use
SetIgnoreReadOnlyProps(True)to omit properties that only have areadclause during serialization. - Map/Dictionary Sort Order: Control key ordering with
natural,reverse,alphabetical, orreverse-alphabetical. - Custom Serializers: Register custom logic for specific types.
- Word Case: Set the JSON key casing using options like
Configure Neon settings
masterNeon uses an
INeonConfigurationinterface (instantiated viaTNeonConfiguration.Default) to define how serialization behaves. You can configure member casing, which members to include, field prefixes, visibility, and custom serializers.Case Settings
TNeonCaseaffects both member names and enum names:TNeonCase.CamelCase: ConvertsAdminto"admin".TNeonCase.SnakeCase: ConvertsVeryHighSpeedto"very_high_speed".- Note: Explicit
[NeonEnumNames(...)]or[NeonProperty]attributes take precedence over these settings.
Configuration Options
.SetMemberCase(TNeonCase): Sets the naming convention..SetMembers(TNeonMembers): Defines which member types to include (e.g.,TNeonMembers.Properties)..SetIgnoreFieldPrefix(Boolean): Handles prefixes likeF(common in Delphi fields)..SetVisibility([TVisibility]): Controls which RTTI visibility levels are processed (e.g.,mvPublic,mvPublished)..RegisterSerializer(TSerializer): Registers a custom serializer for specific types.
var LConfig: INeonConfiguration; begin LConfig := TNeonConfiguration.Default .SetMemberCase(TNeonCase.SnakeCase) // Case settings .SetMembers(TNeonMembers.Properties) // Member type settings .SetIgnoreFieldPrefix(True) // F Prefix settings .SetVisibility([mvPublic, mvPublished]) // Visibility settings // Custom serializer registration .RegisterSerializer(TGUIDSerializer) ; end;Generate Delphi records instead of classes
masterTo generate Delphi
recordtypes instead ofclasstypes, use the-k recordswitch.Note: Records own nothing, so the generator will not produce constructors or destructors, and arrays of entities will always be generated as
TArray<T>regardless of the--arraysetting... \.. extbackslash Bin\Classify -k record --no-header ..\.. extbackslash Data\customer.jsonSerialize and deserialize objects using TNeon
masterThe
TNeonutility class provides the simplest way to perform JSON operations. UseTNeon.ObjectToJSONto convert an object to aTJSONValueandTNeon.JSONToObjectto populate an object from aTJSONValueusing a configuration object.var LJSON: TJSONValue; begin // Serialization LJSON := TNeon.ObjectToJSON(AObject); try Memo1.Lines.Text := TNeon.Print(LJSON, True); finally LJSON.Free; end; end; // Deserialization var LJSON: TJSONValue; begin LJSON := TJSONObject.ParseJSONValue(Memo1.Lines.Text); try TNeon.JSONToObject(AObject, LJSON, AConfig); finally LJSON.Free; end; end;