From b0c6db675ef57848c9aa7bdb5614193fc241fecf Mon Sep 17 00:00:00 2001 From: JamesNK Date: Sat, 27 Mar 2010 06:18:40 +0000 Subject: [PATCH] -Added WriteRegex to BsonWriter -Added RegexConverter -Fixed BsonWriter writing empty string instead of null --- .../Bson/BsonReaderTests.cs | 35 ++++ .../Bson/BsonWriterTests.cs | 86 ++++++++++ .../Converters/RegexConverterTests.cs | 156 ++++++++++++++++++ .../Newtonsoft.Json.Tests.Compact.csproj | 2 + .../Newtonsoft.Json.Tests.Net20.csproj | 2 + .../Newtonsoft.Json.Tests.Silverlight.csproj | 2 + .../Newtonsoft.Json.Tests.csproj | 1 + .../Serialization/JsonSerializerTest.cs | 1 + Src/Newtonsoft.Json/Bson/BsonBinaryWriter.cs | 59 ++++--- Src/Newtonsoft.Json/Bson/BsonToken.cs | 121 ++++++++++++++ Src/Newtonsoft.Json/Bson/BsonWriter.cs | 150 ++++------------- .../Converters/RegexConverter.cs | 127 ++++++++++++++ .../Newtonsoft.Json.Compact.csproj | 2 + .../Newtonsoft.Json.Net20.csproj | 2 + .../Newtonsoft.Json.Silverlight.csproj | 2 + Src/Newtonsoft.Json/Newtonsoft.Json.csproj | 2 + 16 files changed, 615 insertions(+), 135 deletions(-) create mode 100644 Src/Newtonsoft.Json.Tests/Converters/RegexConverterTests.cs create mode 100644 Src/Newtonsoft.Json/Bson/BsonToken.cs create mode 100644 Src/Newtonsoft.Json/Converters/RegexConverter.cs diff --git a/Src/Newtonsoft.Json.Tests/Bson/BsonReaderTests.cs b/Src/Newtonsoft.Json.Tests/Bson/BsonReaderTests.cs index a6489829..939e3471 100644 --- a/Src/Newtonsoft.Json.Tests/Bson/BsonReaderTests.cs +++ b/Src/Newtonsoft.Json.Tests/Bson/BsonReaderTests.cs @@ -979,5 +979,40 @@ namespace Newtonsoft.Json.Tests.Bson string expected = sb.ToString(); Assert.AreEqual(expected, WriteAndReadStringPropertyName(expected)); } + + [Test] + public void ReadRegexWithOptions() + { + string hexdoc = "1A-00-00-00-0B-72-65-67-65-78-00-61-62-63-00-69-00-0B-74-65-73-74-00-00-00-00"; + + byte[] data = MiscellaneousUtils.HexToBytes(hexdoc); + + MemoryStream ms = new MemoryStream(data); + BsonReader reader = new BsonReader(ms); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("/abc/i", reader.Value); + Assert.AreEqual(typeof(string), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("//", reader.Value); + Assert.AreEqual(typeof(string), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + + Assert.IsFalse(reader.Read()); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Bson/BsonWriterTests.cs b/Src/Newtonsoft.Json.Tests/Bson/BsonWriterTests.cs index e99595ee..ca641326 100644 --- a/Src/Newtonsoft.Json.Tests/Bson/BsonWriterTests.cs +++ b/Src/Newtonsoft.Json.Tests/Bson/BsonWriterTests.cs @@ -472,5 +472,91 @@ namespace Newtonsoft.Json.Tests.Bson Assert.AreEqual(expected, ms.ToArray()); } + + [Test] + public void WriteRegexPlusContent() + { + MemoryStream ms = new MemoryStream(); + BsonWriter writer = new BsonWriter(ms); + + writer.WriteStartObject(); + writer.WritePropertyName("regex"); + writer.WriteRegex("abc", "i"); + writer.WritePropertyName("test"); + writer.WriteRegex(string.Empty, null); + writer.WriteEndObject(); + + byte[] expected = MiscellaneousUtils.HexToBytes("1A-00-00-00-0B-72-65-67-65-78-00-61-62-63-00-69-00-0B-74-65-73-74-00-00-00-00"); + + Assert.AreEqual(expected, ms.ToArray()); + } + + [Test] + public void SerializeEmptyAndNullStrings() + { + Product p = new Product(); + p.ExpiryDate = DateTime.Parse("2009-04-05T14:45:00Z"); + p.Name = null; + p.Price = 9.95m; + p.Sizes = new[] { "Small", "", null }; + + MemoryStream ms = new MemoryStream(); + JsonSerializer serializer = new JsonSerializer(); + + BsonWriter writer = new BsonWriter(ms); + serializer.Serialize(writer, p); + + ms.Seek(0, SeekOrigin.Begin); + + BsonReader reader = new BsonReader(ms); + Product deserializedProduct = serializer.Deserialize(reader); + + Console.WriteLine(deserializedProduct.Name); + + Assert.AreEqual(null, deserializedProduct.Name); + Assert.AreEqual(9.95m, deserializedProduct.Price); + Assert.AreEqual(3, deserializedProduct.Sizes.Length); + Assert.AreEqual("Small", deserializedProduct.Sizes[0]); + Assert.AreEqual("", deserializedProduct.Sizes[1]); + Assert.AreEqual(null, deserializedProduct.Sizes[2]); + } + + [Test] + public void WriteReadEmptyAndNullStrings() + { + MemoryStream ms = new MemoryStream(); + BsonWriter writer = new BsonWriter(ms); + + writer.WriteStartArray(); + writer.WriteValue("Content!"); + writer.WriteValue(""); + writer.WriteValue((string)null); + writer.WriteEndArray(); + + ms.Seek(0, SeekOrigin.Begin); + + BsonReader reader = new BsonReader(ms); + reader.ReadRootValueAsArray = true; + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartArray, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("Content!", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Null, reader.TokenType); + Assert.AreEqual(null, reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndArray, reader.TokenType); + + Assert.IsFalse(reader.Read()); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Converters/RegexConverterTests.cs b/Src/Newtonsoft.Json.Tests/Converters/RegexConverterTests.cs new file mode 100644 index 00000000..7f0b4d86 --- /dev/null +++ b/Src/Newtonsoft.Json.Tests/Converters/RegexConverterTests.cs @@ -0,0 +1,156 @@ +using System; +using System.Collections.Generic; +#if !SILVERLIGHT && !PocketPC && !NET20 +using System.Data.Linq; +#endif +#if !SILVERLIGHT +using System.Data.SqlTypes; +#endif +using System.IO; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using Newtonsoft.Json.Bson; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Utilities; +using NUnit.Framework; +using Newtonsoft.Json.Tests.TestObjects; + +namespace Newtonsoft.Json.Tests.Converters +{ + public class RegexConverterTests : TestFixtureBase + { + public class RegexTestClass + { + public Regex Regex { get; set; } + } + + [Test] + public void SerializeToText() + { + Regex regex = new Regex("abc", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); + + string json = JsonConvert.SerializeObject(regex, Formatting.Indented, new RegexConverter()); + + Assert.AreEqual(@"{ + ""Pattern"": ""abc"", + ""Options"": 513 +}", json); + } + + [Test] + public void SerializeToBson() + { + Regex regex = new Regex("abc", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); + + MemoryStream ms = new MemoryStream(); + BsonWriter writer = new BsonWriter(ms); + JsonSerializer serializer = new JsonSerializer(); + serializer.Converters.Add(new RegexConverter()); + + serializer.Serialize(writer, new RegexTestClass { Regex = regex }); + + string expected = "13-00-00-00-0B-52-65-67-65-78-00-61-62-63-00-69-75-00-00"; + string bson = MiscellaneousUtils.BytesToHex(ms.ToArray()); + + Assert.AreEqual(expected, bson); + } + + [Test] + public void DeserializeFromText() + { + string json = @"{ + ""Pattern"": ""abc"", + ""Options"": 513 +}"; + + Regex newRegex = JsonConvert.DeserializeObject(json, new RegexConverter()); + Assert.AreEqual("abc", newRegex.ToString()); + Assert.AreEqual(RegexOptions.IgnoreCase | RegexOptions.CultureInvariant, newRegex.Options); + } + + [Test] + public void DeserializeFromBson() + { + MemoryStream ms = new MemoryStream(MiscellaneousUtils.HexToBytes("13-00-00-00-0B-52-65-67-65-78-00-61-62-63-00-69-75-00-00")); + BsonReader reader = new BsonReader(ms); + JsonSerializer serializer = new JsonSerializer(); + serializer.Converters.Add(new RegexConverter()); + + RegexTestClass c = serializer.Deserialize(reader); + + Assert.AreEqual("abc", c.Regex.ToString()); + Assert.AreEqual(RegexOptions.IgnoreCase, c.Regex.Options); + } + + [Test] + public void ConvertEmptyRegexBson() + { + Regex regex = new Regex(string.Empty); + + MemoryStream ms = new MemoryStream(); + BsonWriter writer = new BsonWriter(ms); + JsonSerializer serializer = new JsonSerializer(); + serializer.Converters.Add(new RegexConverter()); + + serializer.Serialize(writer, new RegexTestClass { Regex = regex }); + + ms.Seek(0, SeekOrigin.Begin); + BsonReader reader = new BsonReader(ms); + serializer.Converters.Add(new RegexConverter()); + + RegexTestClass c = serializer.Deserialize(reader); + + Assert.AreEqual("", c.Regex.ToString()); + Assert.AreEqual(RegexOptions.None, c.Regex.Options); + } + + [Test] + public void ConvertRegexWithAllOptionsBson() + { + Regex regex = new Regex( + "/", + RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.Multiline | RegexOptions.ExplicitCapture); + + MemoryStream ms = new MemoryStream(); + BsonWriter writer = new BsonWriter(ms); + JsonSerializer serializer = new JsonSerializer(); + serializer.Converters.Add(new RegexConverter()); + + serializer.Serialize(writer, new RegexTestClass { Regex = regex }); + + string expected = "14-00-00-00-0B-52-65-67-65-78-00-2F-00-69-6D-73-75-78-00-00"; + string bson = MiscellaneousUtils.BytesToHex(ms.ToArray()); + + Assert.AreEqual(expected, bson); + + ms.Seek(0, SeekOrigin.Begin); + BsonReader reader = new BsonReader(ms); + serializer.Converters.Add(new RegexConverter()); + + RegexTestClass c = serializer.Deserialize(reader); + + Assert.AreEqual("/", c.Regex.ToString()); + Assert.AreEqual(RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.Multiline | RegexOptions.ExplicitCapture, c.Regex.Options); + } + + [Test] + public void ConvertEmptyRegexJson() + { + Regex regex = new Regex(""); + + string json = JsonConvert.SerializeObject(new RegexTestClass { Regex = regex }, Formatting.Indented, new RegexConverter()); + + Assert.AreEqual(@"{ + ""Regex"": { + ""Pattern"": """", + ""Options"": 0 + } +}", json); + + RegexTestClass newRegex = JsonConvert.DeserializeObject(json, new RegexConverter()); + Assert.AreEqual("", newRegex.Regex.ToString()); + Assert.AreEqual(RegexOptions.None, newRegex.Regex.Options); + } + } +} \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Compact.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Compact.csproj index 7e2ea82e..c8a92c47 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Compact.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Compact.csproj @@ -66,6 +66,8 @@ + + diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj index 7eb545e5..a5382d9e 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj @@ -70,6 +70,8 @@ + + diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj index 638022d4..08b88c78 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj @@ -75,6 +75,8 @@ + + diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj index e2d332d6..121ef0fa 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj @@ -87,6 +87,7 @@ + diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index 0ace8424..2920a097 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -50,6 +50,7 @@ using Newtonsoft.Json.Utilities; using System.Reflection; #if !NET20 && !SILVERLIGHT using System.Xml.Linq; +using System.Text.RegularExpressions; #endif namespace Newtonsoft.Json.Tests.Serialization diff --git a/Src/Newtonsoft.Json/Bson/BsonBinaryWriter.cs b/Src/Newtonsoft.Json/Bson/BsonBinaryWriter.cs index 7ff69655..397702ca 100644 --- a/Src/Newtonsoft.Json/Bson/BsonBinaryWriter.cs +++ b/Src/Newtonsoft.Json/Bson/BsonBinaryWriter.cs @@ -139,6 +139,14 @@ namespace Newtonsoft.Json.Bson _writer.Write(data); } break; + case BsonType.Regex: + { + BsonRegex value = (BsonRegex) t; + + WriteString((string)value.Pattern.Value, value.Pattern.ByteCount, null); + WriteString((string)value.Options.Value, value.Options.ByteCount, null); + } + break; default: throw new ArgumentOutOfRangeException("t", "Unexpected token when writing BSON: {0}".FormatWith(CultureInfo.InvariantCulture, t.Type)); } @@ -149,26 +157,29 @@ namespace Newtonsoft.Json.Bson if (calculatedlengthPrefix != null) _writer.Write(calculatedlengthPrefix.Value); - if (_largeByteBuffer == null) + if (s != null) { - _largeByteBuffer = new byte[256]; - _maxChars = 256 / Encoding.GetMaxByteCount(1); - } - if (byteCount <= 256) - { - Encoding.GetBytes(s, 0, s.Length, _largeByteBuffer, 0); - _writer.Write(_largeByteBuffer, 0, byteCount); - } - else - { - int charCount; - int totalCharsWritten = 0; - for (int i = s.Length; i > 0; i -= charCount) + if (_largeByteBuffer == null) { - charCount = (i > _maxChars) ? _maxChars : i; - int count = Encoding.GetBytes(s, totalCharsWritten, charCount, _largeByteBuffer, 0); - _writer.Write(_largeByteBuffer, 0, count); - totalCharsWritten += charCount; + _largeByteBuffer = new byte[256]; + _maxChars = 256/Encoding.GetMaxByteCount(1); + } + if (byteCount <= 256) + { + Encoding.GetBytes(s, 0, s.Length, _largeByteBuffer, 0); + _writer.Write(_largeByteBuffer, 0, byteCount); + } + else + { + int charCount; + int totalCharsWritten = 0; + for (int i = s.Length; i > 0; i -= charCount) + { + charCount = (i > _maxChars) ? _maxChars : i; + int count = Encoding.GetBytes(s, totalCharsWritten, charCount, _largeByteBuffer, 0); + _writer.Write(_largeByteBuffer, 0, count); + totalCharsWritten += charCount; + } } } @@ -238,7 +249,7 @@ namespace Newtonsoft.Json.Bson { BsonString value = (BsonString)t; string s = (string) value.Value; - value.ByteCount = Encoding.GetByteCount(s); + value.ByteCount = (s != null) ? Encoding.GetByteCount(s) : 0; value.CalculatedSize = CalculateSizeWithLength(value.ByteCount, value.IncludeLength); return value.CalculatedSize; @@ -261,6 +272,16 @@ namespace Newtonsoft.Json.Bson } case BsonType.Oid: return 12; + case BsonType.Regex: + { + BsonRegex value = (BsonRegex) t; + int size = 0; + size += CalculateSize(value.Pattern); + size += CalculateSize(value.Options); + value.CalculatedSize = size; + + return value.CalculatedSize; + } default: throw new ArgumentOutOfRangeException("t", "Unexpected token when writing BSON: {0}".FormatWith(CultureInfo.InvariantCulture, t.Type)); } diff --git a/Src/Newtonsoft.Json/Bson/BsonToken.cs b/Src/Newtonsoft.Json/Bson/BsonToken.cs new file mode 100644 index 00000000..ff4b21da --- /dev/null +++ b/Src/Newtonsoft.Json/Bson/BsonToken.cs @@ -0,0 +1,121 @@ +using System.Collections; +using System.Collections.Generic; + +namespace Newtonsoft.Json.Bson +{ + internal abstract class BsonToken + { + public abstract BsonType Type { get; } + public BsonToken Parent { get; set; } + public int CalculatedSize { get; set; } + } + + internal class BsonObject : BsonToken, IEnumerable + { + private readonly List _children = new List(); + + public void Add(string name, BsonToken token) + { + _children.Add(new BsonProperty { Name = new BsonString(name, false), Value = token }); + token.Parent = this; + } + + public override BsonType Type + { + get { return BsonType.Object; } + } + + public IEnumerator GetEnumerator() + { + return _children.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + + internal class BsonArray : BsonToken, IEnumerable + { + private readonly List _children = new List(); + + public void Add(BsonToken token) + { + _children.Add(token); + token.Parent = this; + } + + public override BsonType Type + { + get { return BsonType.Array; } + } + + public IEnumerator GetEnumerator() + { + return _children.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + + internal class BsonValue : BsonToken + { + private object _value; + private BsonType _type; + + public BsonValue(object value, BsonType type) + { + _value = value; + _type = type; + } + + public object Value + { + get { return _value; } + } + + public override BsonType Type + { + get { return _type; } + } + } + + internal class BsonString : BsonValue + { + public int ByteCount { get; set; } + public bool IncludeLength { get; set; } + + public BsonString(object value, bool includeLength) + : base(value, BsonType.String) + { + IncludeLength = includeLength; + } + } + + internal class BsonRegex : BsonToken + { + public BsonString Pattern { get; set; } + public BsonString Options { get; set; } + + public BsonRegex(string pattern, string options) + { + Pattern = new BsonString(pattern, false); + Options = new BsonString(options, false); + } + + public override BsonType Type + { + get { return BsonType.Regex; } + } + } + + internal class BsonProperty + { + public BsonString Name { get; set; } + public BsonToken Value { get; set; } + } +} \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Bson/BsonWriter.cs b/Src/Newtonsoft.Json/Bson/BsonWriter.cs index 82940a69..076bcbbc 100644 --- a/Src/Newtonsoft.Json/Bson/BsonWriter.cs +++ b/Src/Newtonsoft.Json/Bson/BsonWriter.cs @@ -34,105 +34,6 @@ using System.Globalization; namespace Newtonsoft.Json.Bson { - internal abstract class BsonToken - { - public abstract BsonType Type { get; } - public BsonToken Parent { get; set; } - public int CalculatedSize { get; set; } - } - - internal class BsonObject : BsonToken, IEnumerable - { - private readonly List _children = new List(); - - public void Add(string name, BsonToken token) - { - _children.Add(new BsonProperty { Name = new BsonString(name, false), Value = token }); - token.Parent = this; - } - - public override BsonType Type - { - get { return BsonType.Object; } - } - - public IEnumerator GetEnumerator() - { - return _children.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - } - - internal class BsonArray : BsonToken, IEnumerable - { - private readonly List _children = new List(); - - public void Add(BsonToken token) - { - _children.Add(token); - token.Parent = this; - } - - public override BsonType Type - { - get { return BsonType.Array; } - } - - public IEnumerator GetEnumerator() - { - return _children.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - } - - internal class BsonValue : BsonToken - { - private object _value; - private BsonType _type; - - public BsonValue(object value, BsonType type) - { - _value = value; - _type = type; - } - - public object Value - { - get { return _value; } - } - - public override BsonType Type - { - get { return _type; } - } - } - - internal class BsonString : BsonValue - { - public int ByteCount { get; set; } - public bool IncludeLength { get; set; } - - public BsonString(object value, bool includeLength) - : base(value, BsonType.String) - { - IncludeLength = includeLength; - } - } - - internal class BsonProperty - { - public BsonString Name { get; set; } - public BsonToken Value { get; set; } - } - /// /// Represents a writer that provides a fast, non-cached, forward-only way of generating Json data. /// @@ -213,22 +114,6 @@ namespace Newtonsoft.Json.Bson throw new JsonWriterException("Cannot write raw JSON as BSON."); } - /// - /// Writes a value that represents a BSON object id. - /// - /// - public void WriteObjectId(byte[] value) - { - ValidationUtils.ArgumentNotNull(value, "value"); - - if (value.Length != 12) - throw new Exception("An object id must be 12 bytes"); - - // hack to update the writer state - AutoComplete(JsonToken.Undefined); - AddValue(value, BsonType.Oid); - } - /// /// Writes the beginning of a Json array. /// @@ -319,7 +204,10 @@ namespace Newtonsoft.Json.Bson public override void WriteValue(string value) { base.WriteValue(value); - AddToken(new BsonString(value ?? string.Empty, true)); + if (value == null) + AddValue(null, BsonType.Null); + else + AddToken(new BsonString(value, true)); } /// @@ -490,5 +378,35 @@ namespace Newtonsoft.Json.Bson AddValue(value, BsonType.Binary); } #endregion + + /// + /// Writes a value that represents a BSON object id. + /// + /// + public void WriteObjectId(byte[] value) + { + ValidationUtils.ArgumentNotNull(value, "value"); + + if (value.Length != 12) + throw new Exception("An object id must be 12 bytes"); + + // hack to update the writer state + AutoComplete(JsonToken.Undefined); + AddValue(value, BsonType.Oid); + } + + /// + /// Writes a BSON regex. + /// + /// The regex pattern. + /// The regex options. + public void WriteRegex(string pattern, string options) + { + ValidationUtils.ArgumentNotNull(pattern, "pattern"); + + // hack to update the writer state + AutoComplete(JsonToken.Undefined); + AddToken(new BsonRegex(pattern, options)); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Converters/RegexConverter.cs b/Src/Newtonsoft.Json/Converters/RegexConverter.cs new file mode 100644 index 00000000..21d78d2c --- /dev/null +++ b/Src/Newtonsoft.Json/Converters/RegexConverter.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using Newtonsoft.Json.Bson; + +namespace Newtonsoft.Json.Converters +{ + public class RegexConverter : JsonConverter + { + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + Regex regex = (Regex) value; + + BsonWriter bsonWriter = writer as BsonWriter; + if (bsonWriter != null) + WriteBson(bsonWriter, regex); + else + WriteJson(writer, regex); + } + + private bool HasFlag(RegexOptions options, RegexOptions flag) + { + return ((options & flag) == flag); + } + + private void WriteBson(BsonWriter writer, Regex regex) + { + // Regular expression - The first cstring is the regex pattern, the second + // is the regex options string. Options are identified by characters, which + // must be stored in alphabetical order. Valid options are 'i' for case + // insensitive matching, 'm' for multiline matching, 'x' for verbose mode, + // 'l' to make \w, \W, etc. locale dependent, 's' for dotall mode + // ('.' matches everything), and 'u' to make \w, \W, etc. match unicode. + + string options = null; + + if (HasFlag(regex.Options, RegexOptions.IgnoreCase)) + options += "i"; + + if (HasFlag(regex.Options, RegexOptions.Multiline)) + options += "m"; + + if (HasFlag(regex.Options, RegexOptions.Singleline)) + options += "s"; + + options += "u"; + + if (HasFlag(regex.Options, RegexOptions.ExplicitCapture)) + options += "x"; + + writer.WriteRegex(regex.ToString(), options); + } + + private void WriteJson(JsonWriter writer, Regex regex) + { + writer.WriteStartObject(); + writer.WritePropertyName("Pattern"); + writer.WriteValue(regex.ToString()); + writer.WritePropertyName("Options"); + writer.WriteValue(regex.Options); + writer.WriteEndObject(); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + BsonReader bsonReader = reader as BsonReader; + + if (bsonReader != null) + return ReadBson(bsonReader); + else + return ReadJson(reader); + } + + private object ReadBson(BsonReader reader) + { + string regexText = (string)reader.Value; + int patternOptionDelimiterIndex = regexText.LastIndexOf(@"/"); + + string patternText = regexText.Substring(1, patternOptionDelimiterIndex - 1); + string optionsText = regexText.Substring(patternOptionDelimiterIndex + 1); + + RegexOptions options = RegexOptions.None; + foreach (char c in optionsText) + { + switch (c) + { + case 'i': + options |= RegexOptions.IgnoreCase; + break; + case 'm': + options |= RegexOptions.Multiline; + break; + case 's': + options |= RegexOptions.Singleline; + break; + case 'x': + options |= RegexOptions.ExplicitCapture; + break; + } + } + + return new Regex(patternText, options); + } + + private Regex ReadJson(JsonReader reader) + { + reader.Read(); + reader.Read(); + string pattern = (string) reader.Value; + + reader.Read(); + reader.Read(); + int options = Convert.ToInt32(reader.Value); + + reader.Read(); + + return new Regex(pattern, (RegexOptions)options); + } + + public override bool CanConvert(Type objectType) + { + return (objectType == typeof (Regex)); + } + } +} diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Compact.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Compact.csproj index 04581ede..0bd69f59 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Compact.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Compact.csproj @@ -61,6 +61,7 @@ + @@ -72,6 +73,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj index 0021f907..9317497b 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj @@ -81,6 +81,7 @@ + @@ -92,6 +93,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj index 4745f601..f11cc1f1 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj @@ -58,6 +58,7 @@ + @@ -72,6 +73,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj index 79acf220..313e58bc 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj @@ -83,6 +83,7 @@ + @@ -94,6 +95,7 @@ +