Skip to content

Commit

Permalink
check in daniels (@sedanwer) changes to auto convert objects into a D…
Browse files Browse the repository at this point in the history
…ictionary if JsConfig.ConvertObjectTypesIntoStringDictionary is true.
  • Loading branch information
mythz committed Feb 14, 2011
1 parent d068502 commit 22e68f7
Show file tree
Hide file tree
Showing 6 changed files with 204 additions and 147 deletions.
41 changes: 32 additions & 9 deletions src/ServiceStack.Text/Common/DeserializeDictionary.cs
Expand Up @@ -15,6 +15,7 @@
using System.Reflection;
using System.Runtime.Serialization;
using System.Text;
using ServiceStack.Text.Json;

namespace ServiceStack.Text.Common
{
Expand All @@ -38,7 +39,7 @@ public static ParseStringDelegate GetParseMethod(Type type)
return ParseStringDictionary;
}

var dictionaryArgs = mapInterface.GetGenericArguments();
var dictionaryArgs = mapInterface.GetGenericArguments();

var keyTypeParseMethod = Serializer.GetParseFn(dictionaryArgs[KeyIndex]);
if (keyTypeParseMethod == null) return null;
Expand All @@ -48,13 +49,13 @@ public static ParseStringDelegate GetParseMethod(Type type)

var createMapType = type.HasAnyTypeDefinitionsOf(typeof(Dictionary<,>), typeof(IDictionary<,>))
? null : type;

return value => ParseDictionaryType(value, createMapType, dictionaryArgs, keyTypeParseMethod, valueTypeParseMethod);
}

public static Dictionary<string, string> ParseStringDictionary(string value)
{
var index = VerifyAndGetStartIndex(value, typeof (Dictionary<string, string>));
var index = VerifyAndGetStartIndex(value, typeof(Dictionary<string, string>));

var result = new Dictionary<string, string>();

Expand All @@ -79,14 +80,17 @@ public static ParseStringDelegate GetParseMethod(Type type)
}

public static IDictionary<TKey, TValue> ParseDictionary<TKey, TValue>(
string value, Type createMapType,
string value, Type createMapType,
ParseStringDelegate parseKeyFn, ParseStringDelegate parseValueFn)
{
var tryToParseItemsAsDictionaries =
JsConfig.ConvertObjectTypesIntoStringDictionary && typeof(TValue) == typeof(object);

var index = VerifyAndGetStartIndex(value, createMapType);

var to = (createMapType == null)
? new Dictionary<TKey, TValue>()
: (IDictionary<TKey, TValue>)ReflectionExtensions.CreateInstance(createMapType);
? new Dictionary<TKey, TValue>()
: (IDictionary<TKey, TValue>)ReflectionExtensions.CreateInstance(createMapType);

if (value == JsWriter.EmptyMap) return to;

Expand All @@ -100,7 +104,26 @@ public static ParseStringDelegate GetParseMethod(Type type)
var mapKey = (TKey)parseKeyFn(keyValue);
var mapValue = (TValue)parseValueFn(elementValue);

to[mapKey] = mapValue;
if (tryToParseItemsAsDictionaries)
{
var mapValueString = mapValue as string;
var tryParseValueAsDictionary = JsonUtils.IsJsString(mapValueString);
if (tryParseValueAsDictionary)
{
var tmpMap = ParseDictionary<TKey, TValue>(mapValueString, createMapType, parseKeyFn, parseValueFn);
to[mapKey] = (tmpMap != null && tmpMap.Count > 0)
? (TValue)tmpMap
: to[mapKey] = mapValue;
}
else
{
to[mapKey] = mapValue;
}
}
else
{
to[mapKey] = mapValue;
}

Serializer.EatItemSeperatorOrMapEndChar(value, ref index);
}
Expand All @@ -120,7 +143,7 @@ private static int VerifyAndGetStartIndex(string value, Type createMapType)
return index;
}

private static readonly Dictionary<string, ParseDictionaryDelegate> ParseDelegateCache
private static readonly Dictionary<string, ParseDictionaryDelegate> ParseDelegateCache
= new Dictionary<string, ParseDictionaryDelegate>();

private delegate object ParseDictionaryDelegate(string value, Type createMapType,
Expand All @@ -138,7 +161,7 @@ private static int VerifyAndGetStartIndex(string value, Type createMapType)
{
var genericMi = mi.MakeGenericMethod(argTypes);
parseDelegate = (ParseDictionaryDelegate)Delegate.CreateDelegate(
typeof(ParseDictionaryDelegate), genericMi);
typeof(ParseDictionaryDelegate), genericMi);

ParseDelegateCache[key] = parseDelegate;
}
Expand Down
2 changes: 1 addition & 1 deletion src/ServiceStack.Text/Env.cs
Expand Up @@ -23,7 +23,7 @@ static Env()
+ (IsMonoTouch ? " MonoTouch" : "");
}

public static decimal ServiceStackVersion = 1.80m;
public static decimal ServiceStackVersion = 1.82m;

public static bool IsUnix { get; set; }

Expand Down
10 changes: 10 additions & 0 deletions src/ServiceStack.Text/JsConfig.cs
@@ -0,0 +1,10 @@
using System;

namespace ServiceStack.Text
{
public static class JsConfig
{
[ThreadStatic]
public static bool ConvertObjectTypesIntoStringDictionary = false;
}
}
252 changes: 130 additions & 122 deletions src/ServiceStack.Text/Json/JsonUtils.cs
@@ -1,123 +1,131 @@
using System.IO;

namespace ServiceStack.Text.Json
{
public static class JsonUtils
{
public const char EscapeChar = '\\';
public const char QuoteChar = '"';
public const string Null = "null";
public const string True = "true";
public const string False = "false";

static readonly char[] EscapeChars = new[]
{
QuoteChar, '\n', '\r', '\t', '"', '\\', '\f', '\b',
};

private const int LengthFromLargestChar = '\\' + 1;
private static readonly bool[] EscapeCharFlags = new bool[LengthFromLargestChar];

static JsonUtils()
{
foreach (var escapeChar in EscapeChars)
{
EscapeCharFlags[escapeChar] = true;
}
}

public static void WriteString(TextWriter writer, string value)
{
if (!HasAnyEscapeChars(value))
{
writer.Write(QuoteChar);
writer.Write(value);
writer.Write(QuoteChar);
return;
}

var hexSeqBuffer = new char[4];
writer.Write(QuoteChar);

var len = value.Length;
for (var i = 0; i < len; i++)
{
switch (value[i])
{
case '\n':
writer.Write("\\n");
continue;

case '\r':
writer.Write("\\r");
continue;

case '\t':
writer.Write("\\t");
continue;

case '"':
case '\\':
writer.Write('\\');
writer.Write(value[i]);
continue;

case '\f':
writer.Write("\\f");
continue;

case '\b':
writer.Write("\\b");
continue;
}

//Is printable char?
if (value[i] >= 32 && value[i] <= 126)
{
writer.Write(value[i]);
continue;
}

// Default, turn into a \uXXXX sequence
IntToHex(value[i], hexSeqBuffer);
writer.Write("\\u");
writer.Write(hexSeqBuffer);
}

writer.Write(QuoteChar);
}

/// <summary>
/// micro optimizations: using flags instead of value.IndexOfAny(EscapeChars)
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
private static bool HasAnyEscapeChars(string value)
{
var len = value.Length;
for (var i = 0; i < len; i++)
{
var c = value[i];
if (c >= LengthFromLargestChar || !EscapeCharFlags[c]) continue;
return true;
}
return false;
}

public static void IntToHex(int intValue, char[] hex)
{
for (var i = 0; i < 4; i++)
{
var num = intValue % 16;

if (num < 10)
hex[3 - i] = (char)('0' + num);
else
hex[3 - i] = (char)('A' + (num - 10));

intValue >>= 4;
}
}
}

using System;
using System.IO;

namespace ServiceStack.Text.Json
{
public static class JsonUtils
{
public const char EscapeChar = '\\';
public const char QuoteChar = '"';
public const string Null = "null";
public const string True = "true";
public const string False = "false";

static readonly char[] EscapeChars = new[]
{
QuoteChar, '\n', '\r', '\t', '"', '\\', '\f', '\b',
};

private const int LengthFromLargestChar = '\\' + 1;
private static readonly bool[] EscapeCharFlags = new bool[LengthFromLargestChar];

static JsonUtils()
{
foreach (var escapeChar in EscapeChars)
{
EscapeCharFlags[escapeChar] = true;
}
}

public static void WriteString(TextWriter writer, string value)
{
if (!HasAnyEscapeChars(value))
{
writer.Write(QuoteChar);
writer.Write(value);
writer.Write(QuoteChar);
return;
}

var hexSeqBuffer = new char[4];
writer.Write(QuoteChar);

var len = value.Length;
for (var i = 0; i < len; i++)
{
switch (value[i])
{
case '\n':
writer.Write("\\n");
continue;

case '\r':
writer.Write("\\r");
continue;

case '\t':
writer.Write("\\t");
continue;

case '"':
case '\\':
writer.Write('\\');
writer.Write(value[i]);
continue;

case '\f':
writer.Write("\\f");
continue;

case '\b':
writer.Write("\\b");
continue;
}

//Is printable char?
if (value[i] >= 32 && value[i] <= 126)
{
writer.Write(value[i]);
continue;
}

// Default, turn into a \uXXXX sequence
IntToHex(value[i], hexSeqBuffer);
writer.Write("\\u");
writer.Write(hexSeqBuffer);
}

writer.Write(QuoteChar);
}

/// <summary>
/// micro optimizations: using flags instead of value.IndexOfAny(EscapeChars)
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
private static bool HasAnyEscapeChars(string value)
{
var len = value.Length;
for (var i = 0; i < len; i++)
{
var c = value[i];
if (c >= LengthFromLargestChar || !EscapeCharFlags[c]) continue;
return true;
}
return false;
}

public static void IntToHex(int intValue, char[] hex)
{
for (var i = 0; i < 4; i++)
{
var num = intValue % 16;

if (num < 10)
hex[3 - i] = (char)('0' + num);
else
hex[3 - i] = (char)('A' + (num - 10));

intValue >>= 4;
}
}

public static bool IsJsString(string value)
{
return !string.IsNullOrEmpty(value)
&& value[0] == '{'
&& value[value.Length - 1] == '}';
}
}

}
1 change: 1 addition & 0 deletions src/ServiceStack.Text/ServiceStack.Text.csproj
Expand Up @@ -49,6 +49,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="AssemblyUtils.cs" />
<Compile Include="JsConfig.cs" />
<Compile Include="Common\DeserializeSpecializedCollections.cs" />
<Compile Include="Common\JsReader.cs" />
<Compile Include="Common\JsState.cs" />
Expand Down

0 comments on commit 22e68f7

Please sign in to comment.