1 // 2 // http://code.google.com/p/servicestack/wiki/TypeSerializer 3 // ServiceStack.Text: .NET C# POCO Type Text Serializer. 4 // 5 // Authors: 6 // Demis Bellot (demis.bellot@gmail.com) 7 // 8 // Copyright 2011 Liquidbit Ltd. 9 // 10 // Licensed under the same terms of ServiceStack: new BSD license. 11 // 12 13 using System; 14 using System.Collections; 15 using System.Collections.Generic; 16 using System.IO; 17 using System.Reflection; 18 using System.Threading; 19 using ServiceStack.Text.Json; 20 21 namespace ServiceStack.Text.Common 22 { WriteMapDelegate( TextWriter writer, object oMap, WriteObjectDelegate writeKeyFn, WriteObjectDelegate writeValueFn)23 internal delegate void WriteMapDelegate( 24 TextWriter writer, 25 object oMap, 26 WriteObjectDelegate writeKeyFn, 27 WriteObjectDelegate writeValueFn); 28 29 internal static class WriteDictionary<TSerializer> 30 where TSerializer : ITypeSerializer 31 { 32 private static readonly ITypeSerializer Serializer = JsWriter.GetTypeSerializer<TSerializer>(); 33 34 internal class MapKey 35 { 36 internal Type KeyType; 37 internal Type ValueType; 38 MapKey(Type keyType, Type valueType)39 public MapKey(Type keyType, Type valueType) 40 { 41 KeyType = keyType; 42 ValueType = valueType; 43 } 44 Equals(MapKey other)45 public bool Equals(MapKey other) 46 { 47 if (ReferenceEquals(null, other)) return false; 48 if (ReferenceEquals(this, other)) return true; 49 return Equals(other.KeyType, KeyType) && Equals(other.ValueType, ValueType); 50 } 51 Equals(object obj)52 public override bool Equals(object obj) 53 { 54 if (ReferenceEquals(null, obj)) return false; 55 if (ReferenceEquals(this, obj)) return true; 56 if (obj.GetType() != typeof(MapKey)) return false; 57 return Equals((MapKey)obj); 58 } 59 GetHashCode()60 public override int GetHashCode() 61 { 62 unchecked 63 { 64 return ((KeyType != null ? KeyType.GetHashCode() : 0) * 397) ^ (ValueType != null ? ValueType.GetHashCode() : 0); 65 } 66 } 67 } 68 69 static Dictionary<MapKey, WriteMapDelegate> CacheFns = new Dictionary<MapKey, WriteMapDelegate>(); 70 71 public static Action<TextWriter, object, WriteObjectDelegate, WriteObjectDelegate> GetWriteGenericDictionary(Type keyType, Type valueType)72 GetWriteGenericDictionary(Type keyType, Type valueType) 73 { 74 WriteMapDelegate writeFn; 75 var mapKey = new MapKey(keyType, valueType); 76 if (CacheFns.TryGetValue(mapKey, out writeFn)) return writeFn.Invoke; 77 78 var genericType = typeof(ToStringDictionaryMethods<,,>).MakeGenericType(keyType, valueType, typeof(TSerializer)); 79 var mi = genericType.GetMethod("WriteIDictionary", BindingFlags.Static | BindingFlags.Public); 80 writeFn = (WriteMapDelegate)Delegate.CreateDelegate(typeof(WriteMapDelegate), mi); 81 82 Dictionary<MapKey, WriteMapDelegate> snapshot, newCache; 83 do 84 { 85 snapshot = CacheFns; 86 newCache = new Dictionary<MapKey, WriteMapDelegate>(CacheFns); 87 newCache[mapKey] = writeFn; 88 89 } while (!ReferenceEquals( 90 Interlocked.CompareExchange(ref CacheFns, newCache, snapshot), snapshot)); 91 92 return writeFn.Invoke; 93 } 94 WriteIDictionary(TextWriter writer, object oMap)95 public static void WriteIDictionary(TextWriter writer, object oMap) 96 { 97 WriteObjectDelegate writeKeyFn = null; 98 WriteObjectDelegate writeValueFn = null; 99 100 writer.Write(JsWriter.MapStartChar); 101 var encodeMapKey = false; 102 103 var map = (IDictionary)oMap; 104 var ranOnce = false; 105 foreach (var key in map.Keys) 106 { 107 var dictionaryValue = map[key]; 108 109 var isNull = (dictionaryValue == null); 110 if (isNull && !JsConfig.IncludeNullValues) continue; 111 112 if (writeKeyFn == null) 113 { 114 var keyType = key.GetType(); 115 writeKeyFn = Serializer.GetWriteFn(keyType); 116 encodeMapKey = Serializer.GetTypeInfo(keyType).EncodeMapKey; 117 } 118 119 if (writeValueFn == null) 120 writeValueFn = Serializer.GetWriteFn(dictionaryValue.GetType()); 121 122 JsWriter.WriteItemSeperatorIfRanOnce(writer, ref ranOnce); 123 124 JsState.WritingKeyCount++; 125 JsState.IsWritingValue = false; 126 127 if (encodeMapKey) 128 { 129 JsState.IsWritingValue = true; //prevent ""null"" 130 writer.Write(JsWriter.QuoteChar); 131 writeKeyFn(writer, key); 132 writer.Write(JsWriter.QuoteChar); 133 } 134 else 135 { 136 writeKeyFn(writer, key); 137 } 138 139 JsState.WritingKeyCount--; 140 141 writer.Write(JsWriter.MapKeySeperator); 142 143 if (isNull) 144 { 145 writer.Write(JsonUtils.Null); 146 } 147 else 148 { 149 JsState.IsWritingValue = true; 150 writeValueFn(writer, dictionaryValue); 151 JsState.IsWritingValue = false; 152 } 153 } 154 155 writer.Write(JsWriter.MapEndChar); 156 } 157 } 158 159 internal static class ToStringDictionaryMethods<TKey, TValue, TSerializer> 160 where TSerializer : ITypeSerializer 161 { 162 private static readonly ITypeSerializer Serializer = JsWriter.GetTypeSerializer<TSerializer>(); 163 WriteIDictionary( TextWriter writer, object oMap, WriteObjectDelegate writeKeyFn, WriteObjectDelegate writeValueFn)164 public static void WriteIDictionary( 165 TextWriter writer, 166 object oMap, 167 WriteObjectDelegate writeKeyFn, 168 WriteObjectDelegate writeValueFn) 169 { 170 if (writer == null) return; //AOT 171 WriteGenericIDictionary(writer, (IDictionary<TKey, TValue>)oMap, writeKeyFn, writeValueFn); 172 } 173 WriteGenericIDictionary( TextWriter writer, IDictionary<TKey, TValue> map, WriteObjectDelegate writeKeyFn, WriteObjectDelegate writeValueFn)174 public static void WriteGenericIDictionary( 175 TextWriter writer, 176 IDictionary<TKey, TValue> map, 177 WriteObjectDelegate writeKeyFn, 178 WriteObjectDelegate writeValueFn) 179 { 180 if (map == null) 181 { 182 writer.Write(JsonUtils.Null); 183 return; 184 } 185 writer.Write(JsWriter.MapStartChar); 186 187 var encodeMapKey = Serializer.GetTypeInfo(typeof(TKey)).EncodeMapKey; 188 189 var ranOnce = false; 190 foreach (var kvp in map) 191 { 192 var isNull = (kvp.Value == null); 193 if (isNull && !JsConfig.IncludeNullValues) continue; 194 195 JsWriter.WriteItemSeperatorIfRanOnce(writer, ref ranOnce); 196 197 JsState.WritingKeyCount++; 198 JsState.IsWritingValue = false; 199 200 if (encodeMapKey) 201 { 202 JsState.IsWritingValue = true; //prevent ""null"" 203 writer.Write(JsWriter.QuoteChar); 204 writeKeyFn(writer, kvp.Key); 205 writer.Write(JsWriter.QuoteChar); 206 } 207 else 208 { 209 writeKeyFn(writer, kvp.Key); 210 } 211 212 JsState.WritingKeyCount--; 213 214 writer.Write(JsWriter.MapKeySeperator); 215 216 if (isNull) 217 { 218 writer.Write(JsonUtils.Null); 219 } 220 else 221 { 222 JsState.IsWritingValue = true; 223 writeValueFn(writer, kvp.Value); 224 JsState.IsWritingValue = false; 225 } 226 } 227 228 writer.Write(JsWriter.MapEndChar); 229 } 230 } 231 }