Skip to content

Commit f63daea

Browse files
BorisDogadmilazz
andauthored
CSHARP-2353: BsonDocument.DeepClone() fails on documents with duplicate element names (#1283)
Co-authored-by: Adam Milazzo <[email protected]>
1 parent a47542b commit f63daea

File tree

2 files changed

+80
-4
lines changed

2 files changed

+80
-4
lines changed

src/MongoDB.Bson/ObjectModel/BsonDocument.cs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,22 @@ public BsonDocument(BsonElement element)
6868
Add(element);
6969
}
7070

71+
/// <summary>
72+
/// Initializes a new instance of the BsonDocument by coping elements from another BsonDocument.
73+
/// </summary>
74+
/// <param name="document">The document whose elements will be copied</param>
75+
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
76+
public BsonDocument(BsonDocument document)
77+
{
78+
if (document == null)
79+
{
80+
throw new ArgumentNullException(nameof(document));
81+
}
82+
83+
_allowDuplicateNames = document.AllowDuplicateNames;
84+
AddRange(document);
85+
}
86+
7187
/// <summary>
7288
/// Initializes a new instance of the BsonDocument class and adds new elements from a dictionary of key/value pairs.
7389
/// </summary>
@@ -733,7 +749,7 @@ public virtual void Clear()
733749
/// <returns>A shallow clone of the document.</returns>
734750
public override BsonValue Clone()
735751
{
736-
BsonDocument clone = new BsonDocument();
752+
BsonDocument clone = new BsonDocument() { AllowDuplicateNames = AllowDuplicateNames };
737753
foreach (BsonElement element in _elements)
738754
{
739755
clone.Add(element.Clone());
@@ -745,7 +761,7 @@ public override BsonValue Clone()
745761
/// Compares this document to another document.
746762
/// </summary>
747763
/// <param name="rhs">The other document.</param>
748-
/// <returns>A 32-bit signed integer that indicates whether this document is less than, equal to, or greather than the other.</returns>
764+
/// <returns>A 32-bit signed integer that indicates whether this document is less than, equal to, or greater than the other.</returns>
749765
public virtual int CompareTo(BsonDocument rhs)
750766
{
751767
if (rhs == null) { return 1; }
@@ -776,7 +792,7 @@ public virtual int CompareTo(BsonDocument rhs)
776792
/// Compares the BsonDocument to another BsonValue.
777793
/// </summary>
778794
/// <param name="other">The other BsonValue.</param>
779-
/// <returns>A 32-bit signed integer that indicates whether this BsonDocument is less than, equal to, or greather than the other BsonValue.</returns>
795+
/// <returns>A 32-bit signed integer that indicates whether this BsonDocument is less than, equal to, or greater than the other BsonValue.</returns>
780796
public override int CompareTo(BsonValue other)
781797
{
782798
if (other == null) { return 1; }
@@ -818,7 +834,7 @@ public virtual bool ContainsValue(BsonValue value)
818834
/// <returns>A deep clone of the document.</returns>
819835
public override BsonValue DeepClone()
820836
{
821-
BsonDocument clone = new BsonDocument();
837+
BsonDocument clone = new BsonDocument() { AllowDuplicateNames = AllowDuplicateNames };
822838
foreach (BsonElement element in _elements)
823839
{
824840
clone.Add(element.DeepClone());

tests/MongoDB.Bson.Tests/ObjectModel/BsonDocumentTests.cs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,21 @@ public void TestClone()
137137
var clone = (BsonDocument)document.Clone();
138138
Assert.Equal(clone, document);
139139
Assert.Same(clone["d"], document["d"]);
140+
Assert.False(clone.AllowDuplicateNames);
141+
}
142+
143+
[Fact]
144+
public void TestClone_with_duplicate_elements()
145+
{
146+
var document = new BsonDocument(allowDuplicateNames: true)
147+
{
148+
{ "d", new BsonDocument("x", 1) },
149+
{ "d", new BsonDocument("x", 2) },
150+
};
151+
152+
var clone = (BsonDocument)document.Clone();
153+
clone.Should().Be(document);
154+
clone.AllowDuplicateNames.Should().BeTrue();
140155
}
141156

142157
[Fact]
@@ -153,6 +168,7 @@ public void TestConstructorElement()
153168
{
154169
var element = new BsonElement("x", 1);
155170
var document = new BsonDocument(element);
171+
Assert.False(document.AllowDuplicateNames);
156172
Assert.Equal(1, document.ElementCount);
157173
Assert.Equal(1, document["x"].AsInt32);
158174
Assert.Equal(true, document.Contains("x"));
@@ -168,6 +184,7 @@ public void TestConstructorElements()
168184
new BsonElement("y", 2)
169185
};
170186
var document = new BsonDocument((IEnumerable<BsonElement>)elements);
187+
Assert.False(document.AllowDuplicateNames);
171188
Assert.Equal(2, document.ElementCount);
172189
Assert.Equal(1, document["x"].AsInt32);
173190
Assert.Equal(2, document["y"].AsInt32);
@@ -184,6 +201,7 @@ public void TestConstructorElementsDocument()
184201
{
185202
var originalDocument = new BsonDocument { { "x", 1 }, { "y", 2 } };
186203
var document = new BsonDocument(originalDocument);
204+
Assert.False(document.AllowDuplicateNames);
187205
Assert.Equal(2, document.ElementCount);
188206
Assert.Equal(1, document["x"].AsInt32);
189207
Assert.Equal(2, document["y"].AsInt32);
@@ -195,6 +213,20 @@ public void TestConstructorElementsDocument()
195213
Assert.Same(originalDocument.GetElement("y").Value, document.GetElement("y").Value);
196214
}
197215

216+
[Fact]
217+
public void TestConstructorElementsDocumentDuplicateNames()
218+
{
219+
var documentA = new BsonDocument(allowDuplicateNames: true)
220+
{
221+
{ "x", 1 },
222+
{ "x", 2 },
223+
};
224+
var documentB = new BsonDocument(documentA);
225+
226+
documentB.AllowDuplicateNames.Should().BeTrue();
227+
documentB.Elements.ShouldAllBeEquivalentTo(documentA.Elements);
228+
}
229+
198230
[Fact]
199231
public void TestConstructorElementsParams()
200232
{
@@ -203,6 +235,8 @@ public void TestConstructorElementsParams()
203235
#pragma warning disable 618
204236
var document = new BsonDocument(element1, element2);
205237
#pragma warning restore
238+
Assert.False(document.AllowDuplicateNames);
239+
Assert.Equal(2, document.ElementCount);
206240
Assert.Equal(2, document.ElementCount);
207241
Assert.Equal(1, document["x"].AsInt32);
208242
Assert.Equal(2, document["y"].AsInt32);
@@ -219,6 +253,7 @@ public void TestConstructorDictionaryGeneric()
219253
{
220254
var dictionary = new Dictionary<string, object> { { "x", 1 } };
221255
var document = new BsonDocument(dictionary);
256+
Assert.False(document.AllowDuplicateNames);
222257
Assert.Equal(1, document.ElementCount);
223258
Assert.Equal(1, document["x"].AsInt32);
224259
Assert.Equal(true, document.Contains("x"));
@@ -233,6 +268,7 @@ public void TestConstructorDictionaryGenericWithKeys()
233268
#pragma warning disable 618
234269
var document = new BsonDocument(dictionary, keys);
235270
#pragma warning restore
271+
Assert.False(document.AllowDuplicateNames);
236272
Assert.Equal(1, document.ElementCount);
237273
Assert.Equal(1, document["x"].AsInt32);
238274
Assert.Equal(true, document.Contains("x"));
@@ -244,6 +280,7 @@ public void TestConstructorIDictionary()
244280
{
245281
var hashtable = (IDictionary)new Hashtable { { "x", 1 } };
246282
var document = new BsonDocument(hashtable);
283+
Assert.False(document.AllowDuplicateNames);
247284
Assert.Equal(1, document.ElementCount);
248285
Assert.Equal(1, document["x"].AsInt32);
249286
Assert.Equal(true, document.Contains("x"));
@@ -255,6 +292,7 @@ public void TestConstructorIDictionaryGeneric()
255292
{
256293
var dictionary = (IDictionary<string, object>)new Dictionary<string, object> { { "x", 1 } };
257294
var document = new BsonDocument(dictionary);
295+
Assert.False(document.AllowDuplicateNames);
258296
Assert.Equal(1, document.ElementCount);
259297
Assert.Equal(1, document["x"].AsInt32);
260298
Assert.Equal(true, document.Contains("x"));
@@ -269,6 +307,7 @@ public void TestConstructorIDictionaryGenericWithKeys()
269307
#pragma warning disable 618
270308
var document = new BsonDocument(dictionary, keys);
271309
#pragma warning restore
310+
Assert.False(document.AllowDuplicateNames);
272311
Assert.Equal(1, document.ElementCount);
273312
Assert.Equal(1, document["x"].AsInt32);
274313
Assert.Equal(true, document.Contains("x"));
@@ -283,6 +322,7 @@ public void TestConstructorIDictionaryWithKeys()
283322
#pragma warning disable 618
284323
var document = new BsonDocument(hashtable, keys);
285324
#pragma warning restore
325+
Assert.False(document.AllowDuplicateNames);
286326
Assert.Equal(1, document.ElementCount);
287327
Assert.Equal(1, document["x"].AsInt32);
288328
Assert.Equal(true, document.Contains("x"));
@@ -293,6 +333,7 @@ public void TestConstructorIDictionaryWithKeys()
293333
public void TestConstructorNameValue()
294334
{
295335
var document = new BsonDocument("x", 1);
336+
Assert.False(document.AllowDuplicateNames);
296337
Assert.Equal(1, document.ElementCount);
297338
Assert.Equal(1, document["x"].AsInt32);
298339
Assert.Equal(true, document.Contains("x"));
@@ -374,6 +415,25 @@ public void TestDeepClone()
374415
var clone = (BsonDocument)document.DeepClone();
375416
Assert.Equal(clone, document);
376417
Assert.NotSame(clone["d"], document["d"]);
418+
Assert.False(((BsonDocument)document["d"]).AllowDuplicateNames);
419+
}
420+
421+
[Fact]
422+
public void TestDeepClone_with_duplicate_elements()
423+
{
424+
var documentWithDuplicateElements = new BsonDocument(allowDuplicateNames: true)
425+
{
426+
{ "x", 1 },
427+
{ "x", 2 }
428+
};
429+
430+
var document = new BsonDocument("d", documentWithDuplicateElements);
431+
var clone = (BsonDocument)document.DeepClone();
432+
var clonedNestedDocument = (BsonDocument)clone["d"];
433+
434+
clonedNestedDocument.Should().NotBeSameAs((BsonDocument)document["d"]);
435+
clonedNestedDocument.Elements.ShouldAllBeEquivalentTo(documentWithDuplicateElements.Elements);
436+
clonedNestedDocument.AllowDuplicateNames.Should().BeTrue();
377437
}
378438

379439
[Fact]

0 commit comments

Comments
 (0)