Browse Source

Fixed CSHARP-78. Default UseCompactRepresentation to true for primitive properties. Added PushBookmark and PopBookmark to BsonReader to support reading ahead. Fixed handling of discriminators in BsonClassMapSerializer. FindString in BsonReader now actually moves the position (use bookmarks now to read ahead).

pull/10/head
rstam 15 years ago
parent
commit
ef50bbefa1
  1. 9
      Bson/DefaultSerializer/BsonClassMap.cs
  2. 48
      Bson/DefaultSerializer/BsonClassMapSerializer.cs
  3. 22
      Bson/DefaultSerializer/BsonPropertyMap.cs
  4. 7
      Bson/DefaultSerializer/Serializers/GenericEnumerableSerializer.cs
  5. 31
      Bson/IO/BsonBinaryReader.cs
  6. 42
      Bson/IO/BsonBinaryReaderContext.cs
  7. 2
      Bson/IO/BsonReader.cs
  8. 2
      BsonUnitTests/BsonUnitTests.csproj
  9. 50
      BsonUnitTests/DefaultSerializer/BsonClassMapTests.cs
  10. 16
      BsonUnitTests/DefaultSerializer/Serializers/NetPrimitiveSerializerTests.cs
  11. 2
      BsonUnitTests/Jira/CSharp71Tests.cs
  12. 49
      BsonUnitTests/Jira/CSharp78Tests.cs

9
Bson/DefaultSerializer/BsonClassMap.cs

@ -161,6 +161,10 @@ namespace MongoDB.Bson.DefaultSerializer {
Type nominalType,
string discriminator
) {
if (discriminator == null) {
return nominalType;
}
// TODO: will there be too much contention on staticLock?
lock (staticLock) {
Type actualType = null;
@ -356,6 +360,11 @@ namespace MongoDB.Bson.DefaultSerializer {
useCompactRepresentationAttribute = (BsonUseCompactRepresentationAttribute) propertyInfo.GetCustomAttributes(typeof(BsonUseCompactRepresentationAttribute), false).FirstOrDefault();
if (useCompactRepresentationAttribute != null) {
propertyMap.SetUseCompactRepresentation(useCompactRepresentationAttribute.UseCompactRepresentation);
} else {
// default useCompactRepresentation to true for primitive property types
if (propertyMap.PropertyType.IsPrimitive) {
propertyMap.SetUseCompactRepresentation(true);
}
}
}
}

48
Bson/DefaultSerializer/BsonClassMapSerializer.cs

@ -48,15 +48,9 @@ namespace MongoDB.Bson.DefaultSerializer {
) {
VerifyNominalType(nominalType);
// peek at the discriminator (if present) to see what class to create an instance for
var discriminator = bsonReader.FindString("_t");
Type actualType;
if (discriminator != null) {
actualType = BsonClassMap.LookupActualType(nominalType, discriminator);
} else {
actualType = nominalType;
}
bsonReader.ReadStartDocument();
var discriminator = PeekDocumentDiscriminator(bsonReader); // returns null if no discriminator found
var actualType = BsonClassMap.LookupActualType(nominalType, discriminator);
var classMap = BsonClassMap.LookupClassMap(actualType);
if (classMap.IsAnonymous) {
throw new InvalidOperationException("Anonymous classes cannot be deserialized");
@ -64,7 +58,6 @@ namespace MongoDB.Bson.DefaultSerializer {
var obj = Activator.CreateInstance(actualType);
var missingElementPropertyMaps = new List<BsonPropertyMap>(classMap.PropertyMaps); // make a copy!
bsonReader.ReadStartDocument();
BsonType bsonType;
string elementName;
while (bsonReader.HasElement(out bsonType, out elementName)) {
@ -75,7 +68,10 @@ namespace MongoDB.Bson.DefaultSerializer {
var propertyMap = classMap.GetPropertyMapForElement(elementName);
if (propertyMap != null) {
object value = propertyMap.Serializer.DeserializeElement(bsonReader, propertyMap.PropertyType, out elementName);
var elementDiscriminator = PeekElementDiscriminator(bsonReader, bsonType, elementName); // returns null if no discriminator found
var actualElementType = BsonClassMap.LookupActualType(propertyMap.PropertyType, elementDiscriminator);
var serializer = propertyMap.GetSerializerForActualType(actualElementType);
object value = serializer.DeserializeElement(bsonReader, propertyMap.PropertyType, out elementName);
propertyMap.Setter(obj, value);
missingElementPropertyMaps.Remove(propertyMap);
} else {
@ -195,6 +191,32 @@ namespace MongoDB.Bson.DefaultSerializer {
#endregion
#region private methods
private string PeekDocumentDiscriminator(
BsonReader bsonReader
) {
bsonReader.PushBookmark();
var discriminator = bsonReader.FindString("_t");
bsonReader.PopBookmark();
return discriminator;
}
private string PeekElementDiscriminator(
BsonReader bsonReader,
BsonType bsonType,
string elementName
) {
if (bsonType == BsonType.Document) {
bsonReader.PushBookmark();
bsonReader.ReadDocumentName(elementName);
bsonReader.ReadStartDocument();
var discriminator = bsonReader.FindString("_t");
bsonReader.PopBookmark();
return discriminator;
} else {
return null;
}
}
private void SerializeProperty(
BsonWriter bsonWriter,
object obj,
@ -209,9 +231,11 @@ namespace MongoDB.Bson.DefaultSerializer {
}
var nominalType = propertyMap.PropertyType;
var actualType = (value == null) ? nominalType : value.GetType();
var serializer = propertyMap.GetSerializerForActualType(actualType);
var elementName = propertyMap.ElementName;
var useCompactRepresentation = propertyMap.UseCompactRepresentation;
propertyMap.Serializer.SerializeElement(bsonWriter, nominalType, elementName, value, useCompactRepresentation);
serializer.SerializeElement(bsonWriter, nominalType, elementName, value, useCompactRepresentation);
}
private void VerifyNominalType(

22
Bson/DefaultSerializer/BsonPropertyMap.cs

@ -81,15 +81,6 @@ namespace MongoDB.Bson.DefaultSerializer {
get;
}
public IBsonSerializer Serializer {
get {
if (serializer == null) {
serializer = BsonSerializer.LookupSerializer(propertyInfo.PropertyType);
}
return serializer;
}
}
public IBsonIdGenerator IdGenerator {
get {
if (idGenerator == null) {
@ -134,6 +125,19 @@ namespace MongoDB.Bson.DefaultSerializer {
this.Setter(obj, defaultValue);
}
public IBsonSerializer GetSerializerForActualType(
Type actualType
) {
if (actualType == PropertyType) {
if (serializer == null) {
serializer = BsonSerializer.LookupSerializer(propertyInfo.PropertyType);
}
return serializer;
} else {
return BsonSerializer.LookupSerializer(actualType);
}
}
public BsonPropertyMap SetDefaultValue(
object defaultValue
) {

7
Bson/DefaultSerializer/Serializers/GenericEnumerableSerializer.cs

@ -71,15 +71,16 @@ namespace MongoDB.Bson.DefaultSerializer {
BsonReader bsonReader,
Type nominalType
) {
ICollection<T> value;
bsonReader.ReadStartDocument();
bsonReader.PushBookmark();
var discriminator = bsonReader.FindString("_t");
bsonReader.PopBookmark();
if (discriminator == null) {
throw new FileFormatException("Discriminator missing");
}
var actualType = BsonClassMap.LookupActualType(nominalType, discriminator);
value = (ICollection<T>) Activator.CreateInstance(actualType); // deserialization requires ICollection<T> instead of IEnumerable<T>
var value = (ICollection<T>) Activator.CreateInstance(actualType); // deserialization requires ICollection<T> instead of just IEnumerable<T>
bsonReader.ReadStartDocument();
bsonReader.VerifyString("_t", discriminator);
bsonReader.ReadArrayName("_v");
bsonReader.ReadStartDocument();

31
Bson/IO/BsonBinaryReader.cs

@ -70,38 +70,26 @@ namespace MongoDB.Bson.IO {
}
}
// this is like ReadString but scans ahead to find a string element with the desired name
public override string FindString(
string name
) {
if (disposed) { throw new ObjectDisposedException("BsonBinaryReader"); }
if (
context.ReadState != BsonReadState.Initial &&
context.ReadState != BsonReadState.Done &&
context.ReadState != BsonReadState.StartDocument
) {
if ((context.ReadState & BsonReadState.Document) == 0) {
string message = string.Format("FindString cannot be called when ReadState is: {0}", context.ReadState);
throw new InvalidOperationException(message);
}
string value = null;
var initialPosition = buffer.Position;
var documentSize = ReadSize();
BsonType bsonType;
while ((bsonType = buffer.ReadBsonType()) != BsonType.EndOfDocument) {
if (buffer.Position >= initialPosition + documentSize) {
throw new FileFormatException("EndOfDocument missing");
}
var elementName = buffer.ReadCString();
if (bsonType == BsonType.String && elementName == name) {
value = buffer.ReadString();
break;
return buffer.ReadString();
} else {
buffer.Position += GetValueSize(bsonType); // skip over value
}
}
buffer.Position = initialPosition;
return value;
return null;
}
public override bool HasElement() {
@ -152,6 +140,17 @@ namespace MongoDB.Bson.IO {
return buffer.PeekBsonType();
}
public override void PopBookmark() {
var bookmark = context.GetBookmark();
buffer.Position = bookmark.BookmarkPosition;
context = bookmark.BookmarkParentContext;
}
public override void PushBookmark() {
context = context.CreateBookmark();
context.BookmarkPosition = buffer.Position;
}
public override void ReadArrayName(
out string name
) {

42
Bson/IO/BsonBinaryReaderContext.cs

@ -25,6 +25,8 @@ namespace MongoDB.Bson.IO {
private int startPosition;
private int size;
private BsonReadState readState;
private bool isBookmark;
private int bookmarkPosition;
#endregion
#region constructors
@ -39,7 +41,21 @@ namespace MongoDB.Bson.IO {
#region internal properties
internal BsonBinaryReaderContext ParentContext {
get { return parentContext; }
get {
if (isBookmark) {
throw new InvalidOperationException("PushBookmark called without matching PopBookmark");
}
return parentContext;
}
}
internal BsonBinaryReaderContext BookmarkParentContext {
get {
if (!isBookmark) {
throw new InvalidOperationException("Context is not a bookmark");
}
return parentContext;
}
}
internal int StartPosition {
@ -52,9 +68,33 @@ namespace MongoDB.Bson.IO {
set { size = value; }
}
internal int BookmarkPosition {
get { return bookmarkPosition; }
set { bookmarkPosition = value; }
}
internal BsonReadState ReadState {
get { return readState; }
}
#endregion
#region internal methods
internal BsonBinaryReaderContext GetBookmark() {
for (var context = this; context != null; context = context.parentContext) {
if (context.isBookmark) {
return context;
}
}
throw new InvalidOperationException("PopBookmark called without matching PushBookmark");
}
internal BsonBinaryReaderContext CreateBookmark() {
var context = new BsonBinaryReaderContext(this, readState);
context.startPosition = startPosition;
context.size = size;
context.isBookmark = true;
return context;
}
#endregion
}
}

2
Bson/IO/BsonReader.cs

@ -75,6 +75,8 @@ namespace MongoDB.Bson.IO {
out string name
);
public abstract BsonType PeekBsonType();
public abstract void PopBookmark();
public abstract void PushBookmark();
public abstract void ReadArrayName(
out string name
);

2
BsonUnitTests/BsonUnitTests.csproj

@ -49,11 +49,13 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="DefaultSerializer\BsonClassMapTests.cs" />
<Compile Include="DefaultSerializer\SerializeFlagsTests.cs" />
<Compile Include="IO\BsonBufferValueStraddlesChunksTests.cs" />
<Compile Include="Jira\CSharp71Tests.cs" />
<Compile Include="Jira\CSharp74Tests.cs" />
<Compile Include="DefaultSerializer\Attributes\BsonAttributeTests.cs" />
<Compile Include="Jira\CSharp78Tests.cs" />
<Compile Include="ObjectModel\BsonDocumentTests.cs" />
<Compile Include="ObjectModel\BsonElementTests.cs" />
<Compile Include="ObjectModel\BsonEqualsTests.cs" />

50
BsonUnitTests/DefaultSerializer/BsonClassMapTests.cs

@ -0,0 +1,50 @@
/* Copyright 2010 10gen Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using NUnit.Framework;
using MongoDB.Bson;
using MongoDB.Bson.IO;
using MongoDB.Bson.Serialization;
using MongoDB.Bson.DefaultSerializer;
namespace MongoDB.BsonUnitTests.DefaultSerializer {
[TestFixture]
public class BsonClassMapTests {
private class C {
public short SD { get; set; }
[BsonUseCompactRepresentation(false)]
public short SF { get; set; }
[BsonUseCompactRepresentation(true)]
public short SC { get; set; }
}
[Test]
public void TestInt16UseCompactRepresentation() {
var classMap = BsonClassMap.RegisterClassMap<C>();
var sdPropertyMap = classMap.GetPropertyMap("SD");
var sfPropertyMap = classMap.GetPropertyMap("SF");
var scPropertyMap = classMap.GetPropertyMap("SC");
Assert.AreEqual(true, sdPropertyMap.UseCompactRepresentation);
Assert.AreEqual(false, sfPropertyMap.UseCompactRepresentation);
Assert.AreEqual(true, scPropertyMap.UseCompactRepresentation);
}
}
}

16
BsonUnitTests/DefaultSerializer/Serializers/NetPrimitiveSerializerTests.cs

@ -29,8 +29,8 @@ namespace MongoDB.BsonUnitTests.DefaultSerializer {
[TestFixture]
public class ByteSerializerTests {
public class TestClass {
[BsonUseCompactRepresentation]
public byte C { get; set; }
[BsonUseCompactRepresentation(false)]
public byte F { get; set; }
}
@ -102,8 +102,8 @@ namespace MongoDB.BsonUnitTests.DefaultSerializer {
[TestFixture]
public class CharSerializerTests {
public class TestClass {
[BsonUseCompactRepresentation]
public char C { get; set; }
[BsonUseCompactRepresentation(false)]
public char F { get; set; }
}
@ -219,8 +219,8 @@ namespace MongoDB.BsonUnitTests.DefaultSerializer {
[TestFixture]
public class Int16SerializerTests {
public class TestClass {
[BsonUseCompactRepresentation]
public short C { get; set; }
[BsonUseCompactRepresentation(false)]
public short F { get; set; }
}
@ -303,8 +303,8 @@ namespace MongoDB.BsonUnitTests.DefaultSerializer {
[TestFixture]
public class SByteSerializerTests {
public class TestClass {
[BsonUseCompactRepresentation]
public sbyte C { get; set; }
[BsonUseCompactRepresentation(false)]
public sbyte F { get; set; }
}
@ -387,8 +387,8 @@ namespace MongoDB.BsonUnitTests.DefaultSerializer {
[TestFixture]
public class SingleSerializerTests {
public class TestClass {
[BsonUseCompactRepresentation]
public float C { get; set; }
[BsonUseCompactRepresentation(false)]
public float F { get; set; }
}
@ -651,8 +651,8 @@ namespace MongoDB.BsonUnitTests.DefaultSerializer {
[TestFixture]
public class UInt16SerializerTests {
public class TestClass {
[BsonUseCompactRepresentation]
public ushort C { get; set; }
[BsonUseCompactRepresentation(false)]
public ushort F { get; set; }
}
@ -720,8 +720,8 @@ namespace MongoDB.BsonUnitTests.DefaultSerializer {
[TestFixture]
public class UInt32SerializerTests {
public class TestClass {
[BsonUseCompactRepresentation]
public uint C { get; set; }
[BsonUseCompactRepresentation(false)]
public uint F { get; set; }
}
@ -808,8 +808,8 @@ namespace MongoDB.BsonUnitTests.DefaultSerializer {
[TestFixture]
public class UInt64SerializerTests {
public class TestClass {
[BsonUseCompactRepresentation]
public ulong C { get; set; }
[BsonUseCompactRepresentation(false)]
public ulong F { get; set; }
}

2
BsonUnitTests/Jira/CSharp71Tests.cs

@ -295,8 +295,6 @@ namespace MongoDB.BsonUnitTests.Jira {
};
#endregion
document.WriteTo("csharp71.bson");
var bson = document.ToBson();
var rehydrated = BsonSerializer.DeserializeDocument<BsonDocument>(bson);
Assert.IsTrue(bson.SequenceEqual(rehydrated.ToBson()));

49
BsonUnitTests/Jira/CSharp78Tests.cs

@ -0,0 +1,49 @@
/* Copyright 2010 10gen Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Xml;
using NUnit.Framework;
using MongoDB.Bson;
using MongoDB.Bson.IO;
using MongoDB.Bson.Serialization;
namespace MongoDB.BsonUnitTests.Jira {
[TestFixture]
public class CSharp78Tests {
private class C {
public short S { get; set; }
public object O { get; set; }
}
[Test]
public void TestShortSerialization() {
var c = new C { S = 1, O = (short) 1 };
var json = c.ToJson();
var expected = "{ 'S' : 1, 'O' : { '_t' : 'System.Int16', '_v' : 1 } }".Replace("'", "\"");
Assert.AreEqual(expected, json);
var bson = c.ToBson();
var rehydrated = BsonSerializer.DeserializeDocument<C>(bson);
Assert.IsTrue(bson.SequenceEqual(rehydrated.ToBson()));
}
}
}
Loading…
Cancel
Save