JSONConsumerUsage topic
Using TJSONConsumerReader with IJSONConsumer interface
Declaration
JSONConsumerUsageDescription
TJSONConsumerReader provides an alternative to event-based parsing by using the IJSONConsumer interface. Instead of assigning event handlers, you implement the interface methods to process JSON elements.
This approach provides a cleaner object-oriented design and can be easier to maintain for complex JSON processing logic.
The workflow involves:
- Create a class that implements IJSONConsumer
- Create a TJSONConsumerReader instance
- Assign your consumer to the Consumer property
- Call Execute to start parsing
Here's an example that builds a simple data structure from JSON:
program JSONConsumerDemo;
{$mode objfpc}{$H+}
uses
Classes, SysUtils, jsonreader, Generics.Collections;
type
TJSONDataExtractor = class(TInterfacedObject, IJSONConsumer)
private
FStack: TList<string>;
FCurrentPath: string;
FData: TStringList;
public
constructor Create;
destructor Destroy; override;
// IJSONConsumer interface
procedure NullValue;
procedure BooleanValue(const AValue: Boolean);
procedure NumberValue(const AValue: string);
procedure FloatValue(const AValue: Double);
procedure Int64Value(const AValue: Int64);
procedure QWordValue(const AValue: QWord);
procedure IntegerValue(const AValue: Integer);
procedure StringValue(const AValue: UTF8String);
procedure KeyName(const AKey: UTF8String);
procedure StartObject;
procedure EndObject;
procedure StartArray;
procedure EndArray;
property Data: TStringList read FData;
end;
constructor TJSONDataExtractor.Create;
begin
inherited Create;
FStack := TList<string>.Create;
FData := TStringList.Create;
end;
destructor TJSONDataExtractor.Destroy;
begin
FStack.Free;
FData.Free;
inherited Destroy;
end;
procedure TJSONDataExtractor.KeyName(const AKey: UTF8String);
begin
if FCurrentPath <> '' then
FCurrentPath := FCurrentPath + '.' + AKey
else
FCurrentPath := AKey;
end;
procedure TJSONDataExtractor.StringValue(const AValue: UTF8String);
begin
if FCurrentPath <> '' then
FData.Values[FCurrentPath] := AValue;
FCurrentPath := '';
end;
procedure TJSONDataExtractor.IntegerValue(const AValue: Integer);
begin
if FCurrentPath <> '' then
FData.Values[FCurrentPath] := IntToStr(AValue);
FCurrentPath := '';
end;
procedure TJSONDataExtractor.BooleanValue(const AValue: Boolean);
begin
if FCurrentPath <> '' then
FData.Values[FCurrentPath] := BoolToStr(AValue, True);
FCurrentPath := '';
end;
procedure TJSONDataExtractor.StartObject;
begin
if FCurrentPath <> '' then
FStack.Add(FCurrentPath);
end;
procedure TJSONDataExtractor.EndObject;
begin
if FStack.Count > 0 then
begin
FCurrentPath := FStack[FStack.Count - 1];
FStack.Delete(FStack.Count - 1);
end
else
FCurrentPath := '';
end;
// Implement remaining interface methods
procedure TJSONDataExtractor.NullValue; begin FCurrentPath := ''; end;
procedure TJSONDataExtractor.NumberValue(const AValue: UTF8String); begin StringValue(AValue); end;
procedure TJSONDataExtractor.FloatValue(const AValue: Double); begin StringValue(FloatToStr(AValue)); end;
procedure TJSONDataExtractor.Int64Value(const AValue: Int64); begin StringValue(IntToStr(AValue)); end;
procedure TJSONDataExtractor.QWordValue(const AValue: QWord); begin StringValue(IntToStr(AValue)); end;
procedure TJSONDataExtractor.StartArray; begin end;
procedure TJSONDataExtractor.EndArray; begin end;
var
Reader: TJSONConsumerReader;
Extractor: TJSONDataExtractor;
JSONData: string;
i: Integer;
begin
JSONData := '{"user": {"name": "Bob Jones", "age": 35, "active": true}, "settings": {"theme": "dark"}}';
Extractor := TJSONDataExtractor.Create;
Reader := TJSONConsumerReader.Create(JSONData, []);
try
Reader.Consumer := Extractor;
Reader.Execute;
Writeln('Extracted data:');
for i := 0 to Extractor.Data.Count - 1 do
Writeln(' ', Extractor.Data[i]);
finally
Reader.Free;
Extractor.Free;
end;
end.
The consumer interface approach is beneficial when you need to:
- Implement complex parsing logic in a structured way
- Reuse the same parsing logic across different parts of your application
- Build data structures incrementally during parsing
- Keep parsing state in a more organized manner
An experiment, built from development snapshots. Not the official Free Pascal or Lazarus documentation.
