Skip to content

Commit e9cb7b7

Browse files
committed
CSHARP-4757: Modify BsonSerializationInfo to handle properties that are grouped in a nested document.
1 parent 612a109 commit e9cb7b7

File tree

6 files changed

+351
-16
lines changed

6 files changed

+351
-16
lines changed

src/MongoDB.Bson/Serialization/BsonSerializationInfo.cs

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515

1616
using System;
1717
using System.Collections;
18+
using System.Collections.Generic;
19+
using System.Linq;
1820
using MongoDB.Bson.IO;
1921

2022
namespace MongoDB.Bson.Serialization
@@ -24,10 +26,26 @@ namespace MongoDB.Bson.Serialization
2426
/// </summary>
2527
public class BsonSerializationInfo
2628
{
29+
#region static
30+
/// <summary>
31+
/// Creates a new instance of the BsonSerializationinfo class with an element path instead of an element name.
32+
/// </summary>
33+
/// <param name="elementPath">The element path.</param>
34+
/// <param name="serializer">The serializer.</param>
35+
/// <param name="nominalType">The nominal type.</param>
36+
/// <returns>A BsonSerializationInfo.</returns>
37+
public static BsonSerializationInfo CreateWithPath(IEnumerable<string> elementPath, IBsonSerializer serializer, Type nominalType)
38+
{
39+
return new BsonSerializationInfo(elementPath.ToList(), serializer, nominalType);
40+
}
41+
#endregion
42+
2743
// private fields
28-
private string _elementName;
29-
private IBsonSerializer _serializer;
30-
private Type _nominalType;
44+
// note: while _elementName could have been modeled with an _elementPath of length 1, treating this is a special case avoids some allocations
45+
private readonly string _elementName;
46+
private readonly IReadOnlyList<string> _elementPath;
47+
private readonly IBsonSerializer _serializer;
48+
private readonly Type _nominalType;
3149

3250
// constructors
3351
/// <summary>
@@ -43,13 +61,35 @@ public BsonSerializationInfo(string elementName, IBsonSerializer serializer, Typ
4361
_nominalType = nominalType;
4462
}
4563

64+
private BsonSerializationInfo(IReadOnlyList<string> elementPath, IBsonSerializer serializer, Type nominalType)
65+
{
66+
_elementPath = elementPath;
67+
_serializer = serializer;
68+
_nominalType = nominalType;
69+
}
70+
4671
// public properties
4772
/// <summary>
48-
/// Gets or sets the dotted element name.
73+
/// Gets the element name.
4974
/// </summary>
5075
public string ElementName
5176
{
52-
get { return _elementName; }
77+
get
78+
{
79+
if (_elementPath != null)
80+
{
81+
throw new InvalidOperationException("When ElementPath is not null you must use it instead.");
82+
}
83+
return _elementName;
84+
}
85+
}
86+
87+
/// <summary>
88+
/// Gets element path.
89+
/// </summary>
90+
public IReadOnlyList<string> ElementPath
91+
{
92+
get { return _elementPath; }
5393
}
5494

5595
/// <summary>
@@ -92,20 +132,21 @@ public object DeserializeValue(BsonValue value)
92132
/// </summary>
93133
/// <param name="newSerializationInfo">The new info.</param>
94134
/// <returns>A new BsonSerializationInfo.</returns>
135+
[Obsolete("This method is no longer relevant because field names are now allowed to contain dots.")]
95136
public BsonSerializationInfo Merge(BsonSerializationInfo newSerializationInfo)
96137
{
97138
string elementName = null;
98-
if (_elementName != null && newSerializationInfo._elementName != null)
139+
if (ElementName != null && newSerializationInfo.ElementName != null)
99140
{
100-
elementName = _elementName + "." + newSerializationInfo._elementName;
141+
elementName = ElementName + "." + newSerializationInfo.ElementName;
101142
}
102-
else if (_elementName != null)
143+
else if (ElementName != null)
103144
{
104-
elementName = _elementName;
145+
elementName = ElementName;
105146
}
106-
else if (newSerializationInfo._elementName != null)
147+
else if (newSerializationInfo.ElementName != null)
107148
{
108-
elementName = newSerializationInfo._elementName;
149+
elementName = newSerializationInfo.ElementName;
109150
}
110151

111152
return new BsonSerializationInfo(

src/MongoDB.Driver/Linq/Linq3Implementation/Misc/DocumentSerializerHelper.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,14 @@ public static MemberSerializationInfo GetMemberSerializationInfo(IBsonSerializer
3232
throw new InvalidOperationException($"Serializer for {serializer.ValueType} does not have a member named {memberName}.");
3333
}
3434

35-
return new MemberSerializationInfo(serializationInfo.ElementName, serializationInfo.Serializer);
35+
if (serializationInfo.ElementPath == null)
36+
{
37+
return new MemberSerializationInfo(serializationInfo.ElementName, serializationInfo.Serializer);
38+
}
39+
else
40+
{
41+
return new MemberSerializationInfo(serializationInfo.ElementPath, serializationInfo.Serializer);
42+
}
3643
}
3744

3845
public static bool HasMemberSerializationInfo(IBsonSerializer serializer, string memberName)

src/MongoDB.Driver/Linq/Linq3Implementation/Misc/MemberSerializationInfo.cs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
* limitations under the License.
1414
*/
1515

16+
using System;
17+
using System.Collections.Generic;
1618
using MongoDB.Bson.Serialization;
1719
using MongoDB.Driver.Core.Misc;
1820

@@ -22,6 +24,7 @@ internal class MemberSerializationInfo
2224
{
2325
// private fields
2426
private readonly string _elementName;
27+
private readonly IReadOnlyList<string> _elementPath;
2528
private readonly IBsonSerializer _serializer;
2629

2730
// constructors
@@ -31,8 +34,27 @@ public MemberSerializationInfo(string elementName, IBsonSerializer serializer)
3134
_serializer = Ensure.IsNotNull(serializer, nameof(serializer));
3235
}
3336

37+
public MemberSerializationInfo(IReadOnlyList<string> elementPath, IBsonSerializer serializer)
38+
{
39+
_elementPath = Ensure.IsNotNull(elementPath, nameof(elementPath));
40+
_serializer = Ensure.IsNotNull(serializer, nameof(serializer));
41+
}
42+
3443
// public properties
35-
public string ElementName => _elementName;
44+
public string ElementName
45+
{
46+
get
47+
{
48+
if (_elementPath != null)
49+
{
50+
throw new InvalidOperationException("When ElementPath is not null you must use it instead.");
51+
}
52+
return _elementName;
53+
}
54+
}
55+
56+
public IReadOnlyList<string> ElementPath => _elementPath;
57+
3658
public IBsonSerializer Serializer => _serializer;
3759
}
3860
}

src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MemberExpressionToAggregationExpressionTranslator.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,19 @@ public static AggregationExpression Translate(TranslationContext context, Member
7777
}
7878

7979
var serializationInfo = DocumentSerializerHelper.GetMemberSerializationInfo(containerTranslation.Serializer, member.Name);
80-
var ast = AstExpression.GetField(containerTranslation.Ast, serializationInfo.ElementName);
80+
AstExpression ast;
81+
if (serializationInfo.ElementPath == null)
82+
{
83+
ast = AstExpression.GetField(containerTranslation.Ast, serializationInfo.ElementName);
84+
}
85+
else
86+
{
87+
ast = containerTranslation.Ast;
88+
foreach (var subFieldName in serializationInfo.ElementPath)
89+
{
90+
ast = AstExpression.GetField(ast, subFieldName);
91+
}
92+
}
8193
return new AggregationExpression(expression, ast, serializationInfo.Serializer);
8294
}
8395

src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ToFilterFieldTranslators/MemberExpressionToFilterFieldTranslator.cs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,21 @@ public static AstFilterField Translate(TranslationContext context, MemberExpress
8989
if (fieldSerializer is IBsonDocumentSerializer documentSerializer &&
9090
documentSerializer.TryGetMemberSerializationInfo(memberExpression.Member.Name, out BsonSerializationInfo memberSerializationInfo))
9191
{
92-
var subFieldName = memberSerializationInfo.ElementName;
9392
var subFieldSerializer = memberSerializationInfo.Serializer;
94-
return field.SubField(subFieldName, subFieldSerializer);
93+
if (memberSerializationInfo.ElementPath == null)
94+
{
95+
var subFieldName = memberSerializationInfo.ElementName;
96+
return field.SubField(subFieldName, subFieldSerializer);
97+
}
98+
else
99+
{
100+
var subField = field;
101+
foreach (var subFieldName in memberSerializationInfo.ElementPath)
102+
{
103+
subField = subField.SubField(subFieldName, subFieldSerializer);
104+
}
105+
return subField;
106+
}
95107
}
96108

97109
if (memberExpression.Expression.Type.IsConstructedGenericType &&

0 commit comments

Comments
 (0)