Skip to content

Commit

Permalink
Different string representations.
Browse files Browse the repository at this point in the history
  • Loading branch information
SebastianStehle committed Jul 25, 2022
1 parent 78d972e commit 7e033cb
Show file tree
Hide file tree
Showing 8 changed files with 209 additions and 102 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ public static void Register()
{
try
{

if (isRegistered)
{
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,14 @@ public bool IsDiscriminatorCompatibleWithObjectSerializer

public BsonType Representation { get; }

public BsonInstantSerializer(BsonType representation = BsonType.DateTime)
public BsonInstantSerializer()
: this(BsonType.DateTime)
{
if (representation != BsonType.DateTime && representation != BsonType.Int64 && representation != BsonType.String)
}

public BsonInstantSerializer(BsonType representation)
{
if (representation is not BsonType.DateTime and not BsonType.Int64 and not BsonType.String)
{
throw new ArgumentException("Unsupported representation.", nameof(representation));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
// ==========================================================================

using System.Reflection;
using System.Reflection.Metadata;
using System.Text.Json;
using MongoDB.Bson;
using MongoDB.Bson.Serialization;
Expand All @@ -17,9 +18,11 @@ public static class BsonJsonConvention
{
private static bool isRegistered;

public static JsonSerializerOptions Options { get; set; } = new JsonSerializerOptions(JsonSerializerDefaults.Web);
public static JsonSerializerOptions Options { get; private set; } = new JsonSerializerOptions(JsonSerializerDefaults.Web);

public static void Register(JsonSerializerOptions? options = null)
public static BsonType Representation { get; private set; } = BsonType.Document;

public static void Register(JsonSerializerOptions? options = null, BsonType? representation = null)
{
try
{
Expand All @@ -28,6 +31,11 @@ public static void Register(JsonSerializerOptions? options = null)
Options = options;
}

if (representation != null)
{
Representation = representation.Value;
}

if (isRegistered)
{
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,57 +14,98 @@

namespace Squidex.Infrastructure.MongoDb
{
public sealed class BsonJsonSerializer<T> : ClassSerializerBase<T?> where T : class
public sealed class BsonJsonSerializer<T> : ClassSerializerBase<T?>, IRepresentationConfigurable<BsonJsonSerializer<T>> where T : class
{
public override T? Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
public BsonType Representation { get; }

public BsonType ActualRepresentation
{
var bsonReader = context.Reader;
get => Representation == BsonType.Undefined ? BsonJsonConvention.Representation : Representation;
}

if (bsonReader.GetCurrentBsonType() == BsonType.Null)
public JsonSerializerOptions Options
{
get => BsonJsonConvention.Options;
}

public BsonJsonSerializer()
: this(BsonType.Undefined)
{
}

public BsonJsonSerializer(BsonType representation)
{
if (representation is not BsonType.Undefined and not BsonType.String and not BsonType.Binary)
{
bsonReader.ReadNull();
return null;
throw new ArgumentException("Unsupported representation.", nameof(representation));
}

using var stream = DefaultPools.MemoryStream.GetStream();
Representation = representation;
}

using (var writer = new Utf8JsonWriter(stream))
public override T? Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
{
var reader = context.Reader;

switch (reader.GetCurrentBsonType())
{
FromBson(bsonReader, writer);
}
case BsonType.Null:
reader.ReadNull();
return null;
case BsonType.String:
var valueString = reader.ReadString();
return JsonSerializer.Deserialize<T>(valueString, Options);
case BsonType.Binary:
var valueBinary = reader.ReadBytes();
return JsonSerializer.Deserialize<T>(valueBinary, Options);
default:
using (var stream = DefaultPools.MemoryStream.GetStream())
{
using (var writer = new Utf8JsonWriter(stream))
{
FromBson(reader, writer);
}

stream.Position = 0;
stream.Position = 0;

return JsonSerializer.Deserialize<T>(stream, BsonJsonConvention.Options);
return JsonSerializer.Deserialize<T>(stream, Options);
}
}
}

private static void FromBson(IBsonReader reader, Utf8JsonWriter writer)
{
void ReadDocument()
{
reader.ReadStartDocument();
writer.WriteStartObject();

while (reader.ReadBsonType() != BsonType.EndOfDocument)
{
Read();
writer.WriteStartObject();

while (reader.ReadBsonType() != BsonType.EndOfDocument)
{
Read();
}

writer.WriteEndObject();
}

writer.WriteEndObject();
reader.ReadEndDocument();
}

void ReadArray()
{
reader.ReadStartArray();
writer.WriteStartArray();

while (reader.ReadBsonType() != BsonType.EndOfDocument)
{
Read();
writer.WriteStartArray();

while (reader.ReadBsonType() != BsonType.EndOfDocument)
{
Read();
}

writer.WriteEndArray();
}

writer.WriteEndArray();
reader.ReadEndArray();
}

Expand Down Expand Up @@ -135,11 +176,25 @@ void Read()

public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, T? value)
{
var bsonWriter = context.Writer;
var writer = context.Writer;

using (var jsonDocument = JsonSerializer.SerializeToDocument(value, BsonJsonConvention.Options))
switch (ActualRepresentation)
{
WriteElement(bsonWriter, jsonDocument.RootElement);
case BsonType.String:
var jsonString = JsonSerializer.Serialize(value, args.NominalType, Options);
writer.WriteString(jsonString);
break;
case BsonType.Binary:
var jsonBytes = JsonSerializer.SerializeToUtf8Bytes(value, args.NominalType, Options);
writer.WriteBytes(jsonBytes);
break;
default:
using (var jsonDocument = JsonSerializer.SerializeToDocument(value, args.NominalType, Options))
{
WriteElement(writer, jsonDocument.RootElement);
}

break;
}
}

Expand Down Expand Up @@ -178,6 +233,7 @@ private static void WriteElement(IBsonWriter writer, JsonElement element)
foreach (var property in element.EnumerateObject())
{
writer.WriteName(property.Name.EscapeJson());

WriteElement(writer, property.Value);
}

Expand All @@ -188,5 +244,15 @@ private static void WriteElement(IBsonWriter writer, JsonElement element)
break;
}
}

public BsonJsonSerializer<T> WithRepresentation(BsonType representation)
{
return Representation == representation ? this : new BsonJsonSerializer<T>(representation);
}

IBsonSerializer IRepresentationConfigurable.WithRepresentation(BsonType representation)
{
return WithRepresentation(representation);
}
}
}
5 changes: 4 additions & 1 deletion backend/src/Squidex/Config/Domain/StoreServices.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Caching.Distributed;
using Migrations.Migrations.MongoDb;
using MongoDB.Bson;
using MongoDB.Driver;
using MongoDB.Driver.Core.Extensions.DiagnosticSources;
using Squidex.Assets;
Expand Down Expand Up @@ -176,7 +177,9 @@ public static void AddSquidexStoreServices(this IServiceCollection services, ICo

services.AddInitializer<JsonSerializerOptions>("Serializer (BSON)", jsonSerializerOptions =>
{
BsonJsonConvention.Options = jsonSerializerOptions;
var representation = config.GetValue<BsonType>("store:mongoDB:valueRepresentation");

BsonJsonConvention.Register(jsonSerializerOptions, representation);
}, int.MinValue);
}
});
Expand Down
5 changes: 5 additions & 0 deletions backend/src/Squidex/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,11 @@
// The database for all your other read collections.
"database": "Squidex",

// Defines how key-value-store values are represented in MongoDB (e.g. app, rule, schema).
//
// SUPPORTED: Undefined (Objects), String, Binary (from slow to fast).
"valueRepresentation": "Undefined",

"atlas": {
// The organization id.
"groupId": "",
Expand Down
Loading

0 comments on commit 7e033cb

Please sign in to comment.