Grijjy Foundation |
Unit Grijjy.Bson
DescriptionA light-weight and fast BSON and JSON object model, with support for efficiently parsing and writing in JSON and BSON format. The code in this unit is fully compatible with the BSON and JSON used by MongoDB. It supports all JSON extensions used by MongoDB. However, this unit does not have any dependencies on the MongoDB units and can be used as a stand-alone BSON/JSON library. It is therefore also suitable as a general purpose JSON library. Quick Start var Doc: TgoBsonDocument; A: TgoBsonArray; Json: String; Bson: TBytes; begin Doc := TgoBsonDocument.Create; Doc.Add('Hello', 'World'); A := TgoBsonArray.Create(['awesome', 5.05, 1986]); Doc.Add('BSON', A); Json := Doc.ToJson; // Returns: // { "hello" : "world", // "BSON": ["awesone", 5.05, 1986] } Bson := Doc.ToBson; // Saves to binary BSON Doc := TgoBsonDocument.Parse('{ "Answer" : 42 }'); WriteLn(Doc['Answer']); // Outputs 42 Doc['Answer'] := 'Unknown'; Doc['Pi'] := 3.14; WriteLn(Doc.ToJson); // Outputs { "Answer" : "Unknown", "Pi" : 3.14 } end;
Object Model The root type in the BSON object model is TgoBsonValue. TgoBsonValue is a record type which can hold any type of BSON value. Some implicit class operators make it easy to assign basic types: var Value: TgoBsonValue; begin Value := True; // Creates a Boolean value Value := 1; // Creates an Integer value Value := 3.14; // Creates a Double value Value := 'Foo'; // Creates a String value Value := TBytes.Create(1, 2, 3); // Creates a binary (TBytes) value Value := TgoObjectId.GenerateNewId; // Creates an ObjectId value end;
Note that you can change the type later by assigning a value of another type. You can also go the other way around: var Value: TgoBsonValue; FloatVal: Double; begin Value := 3.14; // Creates a Double value FloatVal := Value; // Uses implicit cast FloatVal := Value.AsDouble; // Or more explicit cast Value := 42; // Creates an Integer value FloatVal := Value; // Converts an Integer BSON value to a Double FloatVal := Value.AsDouble; // Raises exception because types don't match exactly if (Value.BsonType = TgoBsonType.Double) then FloatVal := Value.AsDouble; // Now it is safe to cast // Or identical: if (Value.IsDouble) then FloatVal := Value.AsDouble; end;
Note that the implicit operators will try to convert if the types don't match exactly. For example, a BSON value containing an Integer value can be implicitly converted to a Double. If the conversion fails, it returns a zero value (or empty string). The "As*" methods however will raise an exception if the types don't match exactly. You should use these methods if you know that the type you request matches the value's type exactly. These methods are a bit more efficient than the implicit operators. You can check the value type using the BsonType-property or one of the "Is*" methods. For non-basic types, there are value types that are "derived" from TgoBsonValue:
Note that these are not "real" derived types, since they are implemented as Delphi records (which do not support inheritance). But the implicit operators make it possible to treat each of these types as a TgoBsonValue. For example var MyArray: TgoBsonArray; Value: TgoBsonValue; begin MyArray := TgoBsonArray.Create([1, 3.14, 'Foo', False]); Value := MyArray; // "subtypes" are compatible with TgoBsonValue // Or shorter: Value := TgoBsonArray.Create([1, 3.14, 'Foo', False]); end;
Arrays The example above also shows that arrays can be created very easily. An array contains a collection of BSON values of any type. Since BSON values can be implicitly created from basic types, you can pass multiple types in the array constructor. In the example above, 4 BSON values will be added to the array of types Integer, Double, String and Boolean. You can also add items using the Add or AddRange methods: MyArray := TgoBsonArray.Create; MyArray.Add(1); MyArray.Add(3.14); MyArray.Add('Foo');
Some methods return the array (or document) itself, so they can be used for chaining (aka as a "fluent interface"). The example above is equivalent to: MyArray := TgoBsonArray.Create; MyArray.Add(1).Add(3.14).Add('Foo');
You can change values (and types) like this: // Changes entry 1 from Double to Boolean MyArray[1] := True;
Documents Documents (or dictionaries) can also be created easily: var Doc: TgoBsonDocument; begin Doc := TgoBsonDocument.Create('Answer', 42); end;
This creates a document with a single entry called 'Answer' with a value of 42. Keep in mind that the value can be any BSON type: Doc := TgoBsonDocument.Create('Answer', TgoBsonArray.Create([42, False]));
You can Add, Remove and Delete (Adds can be fluent): Doc := TgoBsonDocument.Create; Doc.Add('Answer', 42); Doc.Add('Pi', 3.14).Add('Pie', 'Yummi'); // Deletes second item (Pi): Doc.Delete(1); // Removes first item (Answer): Doc.Remove('Answer');
Like Delphi dictionaries, the Add method will raise an exception if an item with the given name already exists. Unlike Delphi dictionaries however, you can easily set an item using its default accessor: // Adds Answer: Doc['Answer'] := 42; // Adds Pi: Doc['Pi'] := 3.14; // Updates Answer: Doc['Answer'] := 'Everything';
This adds the item if it does not yet exists, or replaces it otherwise (there is no (need for an) AddOrSet method). Also unlike Delphi dictionaries, documents maintain insertion order and you can also access the items by index: // Returns item by name: V := Doc['Pi']; // Returns item by index: V := Doc.Values[1];
Documents can be easily parsed from JSON:
Doc := TgoBsonDocument.Parse('{ "Answer" : 42 }');
The parser understands standard JSON as well as the MongoDB JSON extensions. You can also load a document from a BSON byte array: Bytes := LoadSomeBSONData(); Doc := TgoBsonDocument.Load(Bytes);
These methods will raise exceptions if the JSON or BSON data is invalid. Memory Management All memory management in this library is automatic. You never need to (and you never must) destroy any objects yourself. The object model types (TgoBsonValue and friends) are all Delphi records. The actual implementations of these records use interfaces to manage memory. There is no concept of ownership in the object model. An array does not own its elements and a document does not own its elements. So you are free to add the same value to multiple arrays and/or documents without ownership concerns: var Array1, Array2, SubArray, Doc1, Doc2: TgoBsonValue; begin SubArray := TgoBsonArray.Create([42, 'Foo', True]); Array1 := TgoBsonArray.Create; Array2 := TgoBsonArray.Create([123, 'Abc']); Doc1 := TgoBsonDocument.Create; Doc2 := TgoBsonDocument.Create('Pi', 3.14); Array1.Add(SubArray); Array2.Add(SubArray); // Add same value to other container Doc1.Add('Bar', SubArray); // And again Doc2.Add('Baz', SubArray); // And again end;
Non-object model types are defined as interfaces, so their memory is managed automatically as well. For example JSON/BSON readers and writer are interfaces: var Reader: IgoJsonReader; Value: TgoBsonValue; begin Reader := TgoJsonReader.Create('{ "Pi" : 3.14 }'); Value := Reader.ReadValue; Assert(Value.IsDocument); Assert(Value.AsDocument['Pi'] = 3.14); end;
Just keep in mind that you must always declare your variable (Reader) as an interface type (IgoJsonReader), but you construct it using the class type (TgoJsonReader). JSON and BSON reading and writing For easy storing, all BSON values have methods called ToJson and ToBson to store its value into JSON or BSON format: var A: TgoBsonValue; B: TBytes; begin A := 42; WriteLn(A.ToJson); // Outputs '42' A := 'Foo'; WriteLn(A.ToJson); // Outputs '"Foo"' A := TgoBsonArray.Create([1, 'Foo', True]); WriteLn(A.ToJson); // Outputs '[1, "Foo", true]' A := TgoBsonDocument.Create('Pi', 3.14); WriteLn(A.ToJson); // Outputs '{ "Pi" : 3.14 }' B := A.ToBson; // Outputs document in BSON format end;
When outputting to JSON, you can optionally supply a settings record to customize the output:
If you don's supply any settings, then output will be in Strict JSON format without pretty printing. Easy loading is only supported at the Value, Document and Array level, using the Parse and Load methods: var Doc: TgoBsonDocument; Bytes: TBytes; begin Doc := TgoBsonDocument.Parse('{ "Pi" : 3.14 }'); Bytes := LoadSomeBSONData(); Doc := TgoBsonDocument.Load(Bytes); end;
You can load other types using the IgoJsonReader and IgoBsonReader interfaces: var Reader: IgoBsonReader; Value: TgoBsonValue; Bytes: TBytes; begin Bytes := LoadSomeBSONData(); Reader := TgoBsonReader.Create(Bytes); Value := Reader.ReadValue; end;
The JSON reader and writer supports both the "strict" JSON syntax, as well as the "Mongo Shell" syntax (see https://docs.mongodb.org/manual/reference/mongodb-extended-json/). Extended JSON is supported for both reading and writing. This library supports all the current extensions, as well as some deprecated legacy extensions. The JSON reader accepts both key names with double quotes (as per JSON spec) as without quotes. Manual reading and writing For all situations, the methods ToJson, ToBson, Parse and Load can take care of reading and writing any kind of JSON and BSON data. However, you can use the reading and writing interfaces directly if you want for some reason. One reason may be that you want the fastest performance when creating BSON payloads, without the overhead of creating a document object model in memory. For information, see the unit Grijjy.Data.Bson.IO Serialization For even easier reading and writing, you can use serialization to directory store a Delphi record or object in JSON or BSON format (or convert it to a TgoBsonDocument). For information, see the unit Grijjy.Data.Bson.Serialization OverviewClasses, Interfaces, Objects and Records
Types
DescriptionTypes
Generated by P2PasDoc 0.13.0 on 2017-04-25 12:54:26 |