# The powerful MongoBson library
Back-end development, statistics about these scenarios need to use serialization: 1.
1. object clone by serialization deserialization
2. server-side database storage data, binary
3. distributed server-side, multi-process messages, binary
4. back-end logs, text format
5. various server-side configuration files, text format

There are very, very many C# serialization libraries, protobuf, json and so on. But these serialization libraries should not be readable and small for all scenarios. protobuf does not support complex object structures (cannot use inheritance) and is suitable for messages, but not for database storage and log formats. json is suitable for log formats, but is too big for network messages and data storage. We certainly want a library that can satisfy all the above scenarios for the following reasons.
1. you think about one day your configuration file needs to be saved in the database, you do not need to do the format conversion, the back-end directly to the configuration message sent by the front-end to save to the database, which can not reduce very many errors?
2. one day some server-side configuration files do not use the file format, need to be placed in the database, again, only a few lines of code to complete the migration.
3. one day the back-end server crash, you need to scan the logs for data recovery, the logs for deserialization into C# objects, one by one for processing, and then into objects saved to the database is complete.
4. objects saved in the database, you can directly see the text content, you can do a variety of SQL-like operations
5. Imagine a scenario where a configuration text object, deserialized into memory, sent via network messages, and stored in the database. The whole process is done in one go.

Simply put, it reduces various data conversions, reduces code, improves development efficiency, and improves maintainability. MongoDB library can serialize both text and BSON binary format, and MongoDB itself is a very much used database in the game. Its support features are as follows.
1. support complex inheritance structure
2. support for ignoring certain fields serialization
3. support field default values
4. structure with extra fields can still be deserialized, which is very useful for multi-version protocols
5. support for ISupportInitialize interface, this is a godsend when deserialization
6. support for text json and binary bson serialization
7. MongoDB database support

A brief introduction to the mongo bson library
### 1. Support serialization deserialization into json or bson
```csharp
    public sealed class Player
    {
        public long Id;

        public string Account { get; private set; }

        public long UnitId { get; set; }
    }

    Player player1 = new Player() { Id = 1 };
    string json = player1.ToJson();
    Console.WriteLine($"player1 to json: {json}");
    Console.WriteLine($"player to bson: {player.ToBson().ToHex()}");
    // output:
    // player to json: { "_id" : NumberLong(1), "C" : [], "Account" : null, "UnitId" : NumberLong(0) }
    // player to bson: B000000125F69640001000000000000000A4163636F756E740012556E69744964000000000000000000000000


```
Note that mongo's json is a bit different from the standard json, if you want to use the standard json, you can pass in a JsonWriterSettings object and restrict the use of JsonOutputMode.Strict mode
```csharp
    // use standard json
    Player player2 = new Player() { Id = 1 };
    Console.WriteLine($"player to json: {player2.ToJson(new JsonWriterSettings() {OutputMode = JsonOutputMode.Strict})}");
    // player to json: { "_id" : 1, "C" : [], "Account" : null, "UnitId" : 0 }
```

Deserialize json:
```csharp
            // deserialize json
        Player player11 = BsonSerializer.Deserialize<Player>(json);
        Console.WriteLine($"player11 to json: {player11.ToJson()}");
```
Deserialize bson:
```csharp
    // deserialize bson
    using (MemoryStream memoryStream = new MemoryStream(bson))
    {
        Player player12 = (Player) BsonSerializer.Deserialize(memoryStream, typeof (Player));
        Console.WriteLine($"player12 to json: {player12.ToJson()}");
    }
```

### 2. Some fields can be ignored
[BsonIgnore] This tag is used to disable field serialization.
```csharp
	public sealed class Player
	{
        public long Id;

		[BsonIgnore]
		public string Account { get; private set; }
		
		public long UnitId { get; set; }
    }

    Player player = new Player() { Id = 2, UnitId = 3, Account = "panda"};
	Console.WriteLine($"player to json: {player.ToJson()}");
    // player to json: { "_id" : 2, "UnitId" : 3 }
```
### 3. Support for default values and taking aliases
The [BsonElement] field with this tag will serialize even private fields (only public fields are serialized by default), and the tag can take a string parameter to assign an alias to the field serialization.
```csharp
	public sealed class Player
	{
        public long Id;

		public string Account { get; private set; }

		[BsonElement("UId")]
		public long UnitId { get; set; }
    }
    Player player = new Player() { Id = 2, UnitId = 3, Account = "panda"};
	Console.WriteLine($"player to json: {player.ToJson()}");
    // player to json: { "_id" : 2, "Account" : "panda", "UId" : 3 }
```
### 4. Upgrade version support
[BsonIgnoreExtraElements] This tag is used on top of class, used to ignore extra fields when deserializing, general version compatibility needs to be considered, low version of the protocol needs to be able to deserialize
otherwise the new version adds fields, the old version structure deserialization will be wrong
```csharp
	[BsonIgnoreExtraElements]
	public sealed class Player
	public
        public long Id;

		public string Account { get; private set; }

		[BsonElement("UId")
		public long UnitId { get; set; }
    }
```
### 5. Support for complex inheritance structures
The power of the mongo bson library is that it fully supports serialization deserialization inheritance structures. Note that inheritance deserialization requires registration of all parent classes, and there are two ways to do this.
a. You can declare the inherited subclasses on top of the parent class using the [BsonKnownTypes] tag, so that mongo will automatically register them, e.g.:
```csharp
    [BsonKnownTypes(typeof(Entity))]
    public class Component
    {
    }
    [BsonKnownTypes(typeof(Player))]
    public class Entity: Component
    {
    }
    public sealed class Player: Entity
    {
        public long Id;
        
        public string Account { get; set; }
		
        public long UnitId { get; set; }
    }
```
This is flawed because the framework doesn't know what subclasses a class will have, and this is invasive to the framework code, and we want to uncouple this
. You can scan the assembly for the types of all subclass parents and register them with the mongo driver
```csharp
			Type[] types = typeof(Game).Assembly.GetTypes();
			foreach (Type type in types)
			{
				if (!type.IsSubclassOf(typeof(Component))))
				{
					continue;
				}

				BsonClassMap.LookupClassMap(type);
			}

			BsonSerializer.RegisterSerializer(new EnumSerializer<NumericType>(BsonType.String));
```
This completely automates the registration and the user does not need to relate whether the class is registered or not.

### 6. ISupportInitialize interface
mongo bson deserialization supports an ISupportInitialize interface, ISupportInitialize has two methods
```csharp
    public interface ISupportInitialize
    {
        void BeginInit();
        void EndInit();
    }
```
BeginInit is called before deserialization and EndInit is called after deserialization. This interface is very useful now to perform some operations after deserialization. For example
```csharp
	[BsonIgnoreExtraElements]
	public class InnerConfig: AConfigComponent
	{
		[BsonIgnore]
		public IPEndPoint IPEndPoint { get; private set; }
		
		public string Address { get; set; }

		public override void EndInit()
		{
			this.IPEndPoint = NetworkHelper.ToIPEndPoint(this.Address);
		}
	}
````
InnerConfig is the configuration of the process inner network address in ET. Since IPEndPoint is not very configurable, we can configure it as a string and then convert the string to IPEndPoint in EndInit when deserializing.
I also added this call to the protobuf deserialization method, refer to ProtobufHelper.cs, ET's protobuf because to support ilruntime, so remove the map support, if we want a map how to do? Here I gave the generated code are done, the proto messages are changed to a partial class, so that we can extend the class themselves, for example.
```csharp
message UnitInfo
{
	int64 UnitId = 1;

	float X = 2;
	float Y = 3;
	float Z = 4;
}

// protobuf
message G2C_EnterMap // IResponse
{
	int32 RpcId = 90;
	int32 Error = 91;
	string Message = 92;
	// own unit id
	int64 UnitId = 1;
	// all the units
	repeated UnitInfo Units = 2;
}
```
This network message has a repeated UnitInfo field, which is actually an array in protobuf, which is not very convenient to use, I want to convert it to a Dictionary<Int64, UnitInfo> field, we can do something like this
```csharp
    public partial class G2C_EnterMap: ISupportInitialize
    {
        public Dictionary<Int64, UnitInfo> unitsDict = new Dictionary<long, UnitInfo>();
        
        public void BeginInit()
        {
        }

        public void EndInit()
        {
            foreach (var unit in this.Units)
            {
                this.unitsDict.Add(unit.UnitId, unit);
            }
        }
    }
```
The message is extended by such a piece of code, and after deserialization, it is automatically converted into a Dictionary.