Json.NET
Code Coverage Statistics for Source File

Newtonsoft.Json\Serialization\JsonSerializerInternalWriter.cs

Symbol Coverage: 97.08% (233 of 240)

Branch Coverage: 94.16% (145 of 154)

Cyclomatic Complexity Avg: 4.70 Max:18

Code Lines: 244


L V Source
1
#region License
2
// Copyright (c) 2007 James Newton-King
3
//
4
// Permission is hereby granted, free of charge, to any person
5
// obtaining a copy of this software and associated documentation
6
// files (the "Software"), to deal in the Software without
7
// restriction, including without limitation the rights to use,
8
// copy, modify, merge, publish, distribute, sublicense, and/or sell
9
// copies of the Software, and to permit persons to whom the
10
// Software is furnished to do so, subject to the following
11
// conditions:
12
//
13
// The above copyright notice and this permission notice shall be
14
// included in all copies or substantial portions of the Software.
15
//
16
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23
// OTHER DEALINGS IN THE SOFTWARE.
24
#endregion
25

  
26
using System;
27
using System.Collections;
28
using System.Collections.Generic;
29
using System.ComponentModel;
30
using System.Globalization;
31
using System.Linq;
32
using System.Reflection;
33
using System.Runtime.Serialization.Formatters;
34
using Newtonsoft.Json.Linq;
35
using Newtonsoft.Json.Utilities;
36
using System.Runtime.Serialization;
37

  
38
namespace Newtonsoft.Json.Serialization
39
{
40
  internal class JsonSerializerInternalWriter : JsonSerializerInternalBase
41
  {
42
    private JsonSerializerProxy _internalSerializer;
43
    private List<object> _serializeStack;
44

  
45
    private List<object> SerializeStack
46
    {
47
      get
48
      {
49
 720694
        if (_serializeStack == null)
50
 16038
          _serializeStack = new List<object>();
51

  
52
 720694
        return _serializeStack;
53
 720694
      }
54
    }
55

  
56
 16074
    public JsonSerializerInternalWriter(JsonSerializer serializer) : base(serializer)
57
    {
58
 16074
    }
59

  
60
    public void Serialize(JsonWriter jsonWriter, object value)
61
    {
62
 16113
      if (jsonWriter == null)
63
 0
        throw new ArgumentNullException("jsonWriter");
64

  
65
 16113
      SerializeValue(jsonWriter, value, null, GetContractSafe(value));
66
 16107
    }
67

  
68
    private JsonSerializerProxy GetInternalSerializer()
69
    {
70
 130
      if (_internalSerializer == null)
71
 85
        _internalSerializer = new JsonSerializerProxy(this);
72

  
73
 130
      return _internalSerializer;
74
 130
    }
75

  
76
    private JsonContract GetContractSafe(object value)
77
    {
78
 568520
      if (value == null)
79
 25939
        return null;
80

  
81
 542581
      return Serializer.ContractResolver.ResolveContract(value.GetType());
82
 568518
    }
83

  
84
    private void SerializeValue(JsonWriter writer, object value, JsonProperty member, JsonContract contract)
85
    {
86
 568478
      JsonConverter converter = (member != null) ? member.Converter : null;
87

  
88
 568478
      if (value == null)
89
      {
90
 25924
        writer.WriteNull();
91
 25924
        return;
92
      }
93

  
94
 542554
      if ((converter != null
95
 542554
          || ((converter = contract.Converter) != null)
96
 542554
          || ((converter = Serializer.GetMatchingConverter(contract.UnderlyingType)) != null)
97
 542554
          || ((converter = contract.InternalConverter) != null))
98
 542554
        && converter.CanWrite)
99
      {
100
 131
        SerializeConvertable(writer, converter, value, contract);
101
      }
102
 542423
      else if (contract is JsonPrimitiveContract)
103
      {
104
 368804
        writer.WriteValue(value);
105
      }
106
 173619
      else if (contract is JsonStringContract)
107
      {
108
 24
        SerializeString(writer, value, (JsonStringContract) contract);
109
      }
110
 173595
      else if (contract is JsonObjectContract)
111
      {
112
 142251
        SerializeObject(writer, value, (JsonObjectContract) contract, member);
113
      }
114
 31344
      else if (contract is JsonDictionaryContract)
115
      {
116
 10426
        JsonDictionaryContract dictionaryContract = (JsonDictionaryContract) contract;
117
 10426
        SerializeDictionary(writer, dictionaryContract.CreateWrapper(value), dictionaryContract, member);
118
      }
119
 20918
      else if (contract is JsonArrayContract)
120
      {
121
 20908
        if (value is IList)
122
        {
123
 20900
          SerializeList(writer, (IList) value, (JsonArrayContract) contract, member);
124
        }
125
 8
        else if (value is IEnumerable)
126
        {
127
 8
          SerializeList(writer, ((IEnumerable)value).Cast<object>().ToList(), (JsonArrayContract)contract, member);
128
        }
129
        else
130
        {
131
 0
          throw new Exception(
132
 0
            "Cannot serialize '{0}' into a JSON array. Type does not implement IEnumerable.".FormatWith(
133
 0
              CultureInfo.InvariantCulture, value.GetType()));
134
        }
135
      }
136
 10
      else if (contract is JsonLinqContract)
137
      {
138
 8
        ((JToken)value).WriteTo(writer, (Serializer.Converters != null) ? Serializer.Converters.ToArray() : null);
139
      }
140
#if !SILVERLIGHT && !PocketPC
141
 2
      else if (contract is JsonISerializableContract)
142
      {
143
 2
        SerializeISerializable(writer, (ISerializable) value, (JsonISerializableContract) contract);
144
      }
145
#endif
146
 568471
    }
147

  
148
    private bool ShouldWriteReference(object value, JsonProperty property, JsonContract contract)
149
    {
150
 552487
      if (value == null)
151
 25913
        return false;
152
 526574
      if (contract is JsonPrimitiveContract)
153
 368833
        return false;
154

  
155
 157741
      bool? isReference = null;
156

  
157
      // value could be coming from a dictionary or array and not have a property
158
 157741
      if (property != null)
159
 136760
        isReference = property.IsReference;
160

  
161
 157741
      if (isReference == null)
162
 157741
        isReference = contract.IsReference;
163

  
164
 157741
      if (isReference == null)
165
      {
166
 157721
        if (contract is JsonArrayContract)
167
 20903
          isReference = HasFlag(Serializer.PreserveReferencesHandling, PreserveReferencesHandling.Arrays);
168
        else
169
 136818
          isReference = HasFlag(Serializer.PreserveReferencesHandling, PreserveReferencesHandling.Objects);
170
      }
171

  
172
 157741
      if (!isReference.Value)
173
 157703
        return false;
174

  
175
 38
      return Serializer.ReferenceResolver.IsReferenced(value);
176
 552487
    }
177

  
178
    private void WriteMemberInfoProperty(JsonWriter writer, object memberValue, JsonProperty property, JsonContract contract)
179
    {
180
 468818
      string propertyName = property.PropertyName;
181
 468818
      object defaultValue = property.DefaultValue;
182

  
183
 468818
      if (property.NullValueHandling.GetValueOrDefault(Serializer.NullValueHandling) == NullValueHandling.Ignore &&
184
 468818
          memberValue == null)
185
 14
        return;
186

  
187
 468804
      if (property.DefaultValueHandling.GetValueOrDefault(Serializer.DefaultValueHandling) ==
188
 468804
          DefaultValueHandling.Ignore && Equals(memberValue, defaultValue))
189
 8
        return;
190

  
191
 468796
      if (ShouldWriteReference(memberValue, property, contract))
192
      {
193
 4
        writer.WritePropertyName(propertyName);
194
 4
        WriteReference(writer, memberValue);
195
 4
        return;
196
      }
197

  
198
 468792
      if (!CheckForCircularReference(memberValue, property.ReferenceLoopHandling, contract))
199
 1
        return;
200

  
201
 468790
      writer.WritePropertyName(propertyName);
202
 468790
      SerializeValue(writer, memberValue, property, contract);
203
 468817
    }
204

  
205
    private bool CheckForCircularReference(object value, ReferenceLoopHandling? referenceLoopHandling, JsonContract contract)
206
    {
207
 552474
      if (value == null || contract is JsonPrimitiveContract)
208
 352917
        return true;
209

  
210
 199557
      if (SerializeStack.IndexOf(value) != -1)
211
      {
212
 6
        switch (referenceLoopHandling.GetValueOrDefault(Serializer.ReferenceLoopHandling))
213
        {
214
          case ReferenceLoopHandling.Error:
215
 3
            throw new JsonSerializationException("Self referencing loop");
216
          case ReferenceLoopHandling.Ignore:
217
 3
            return false;
218
          case ReferenceLoopHandling.Serialize:
219
 0
            return true;
220
          default:
221
 0
            throw new InvalidOperationException("Unexpected ReferenceLoopHandling value: '{0}'".FormatWith(CultureInfo.InvariantCulture, Serializer.ReferenceLoopHandling));
222
        }
223
      }
224

  
225
 199551
      return true;
226
 552471
    }
227

  
228
    private void WriteReference(JsonWriter writer, object value)
229
    {
230
 13
      writer.WriteStartObject();
231
 13
      writer.WritePropertyName(JsonTypeReflector.RefPropertyName);
232
 13
      writer.WriteValue(Serializer.ReferenceResolver.GetReference(value));
233
 13
      writer.WriteEndObject();
234
 13
    }
235

  
236
    internal static bool TryConvertToString(object value, Type type, out string s)
237
    {
238
#if !PocketPC
239
 38
      TypeConverter converter = ConvertUtils.GetConverter(type);
240

  
241
      // use the objectType's TypeConverter if it has one and can convert to a string
242
 38
      if (converter != null
243
 38
#if !SILVERLIGHT
244
 38
        && !(converter is ComponentConverter)
245
 38
#endif
246
 38
        && converter.GetType() != typeof(TypeConverter))
247
      {
248
 31
        if (converter.CanConvertTo(typeof(string)))
249
        {
250
#if !SILVERLIGHT
251
 31
          s = converter.ConvertToInvariantString(value);
252
#else
253
          s = converter.ConvertToString(value);
254
#endif
255
 31
          return true;
256
        }
257
      }
258
#endif
259

  
260
#if SILVERLIGHT || PocketPC
261
      if (value is Guid || value is Uri || value is TimeSpan)
262
      {
263
        s = value.ToString();
264
        return true;
265
      }
266
#endif
267

  
268
 7
      if (value is Type)
269
      {
270
 3
        s = ((Type)value).AssemblyQualifiedName;
271
 3
        return true;
272
      }
273

  
274
 4
      s = null;
275
 4
      return false;
276
 38
    }
277

  
278
    private void SerializeString(JsonWriter writer, object value, JsonStringContract contract)
279
    {
280
 24
      contract.InvokeOnSerializing(value, Serializer.Context);
281

  
282
      string s;
283
 24
      TryConvertToString(value, contract.UnderlyingType, out s);
284
 24
      writer.WriteValue(s);
285

  
286
 24
      contract.InvokeOnSerialized(value, Serializer.Context);
287
 24
    }
288

  
289
    private void SerializeObject(JsonWriter writer, object value, JsonObjectContract contract, JsonProperty member)
290
    {
291
 142251
      contract.InvokeOnSerializing(value, Serializer.Context);
292

  
293
 142251
      SerializeStack.Add(value);
294
 142251
      writer.WriteStartObject();
295

  
296
 142251
      bool isReference = contract.IsReference ?? HasFlag(Serializer.PreserveReferencesHandling, PreserveReferencesHandling.Objects);
297
 142251
      if (isReference)
298
      {
299
 22
        writer.WritePropertyName(JsonTypeReflector.IdPropertyName);
300
 22
        writer.WriteValue(Serializer.ReferenceResolver.GetReference(value));
301
      }
302
 142251
      if (HasFlag(((member != null) ? member.TypeNameHandling : null) ?? Serializer.TypeNameHandling, TypeNameHandling.Objects))
303
      {
304
 8
        WriteTypeProperty(writer, contract.UnderlyingType);
305
      }
306

  
307
 142251
      int initialDepth = writer.Top;
308

  
309
 142251
      foreach (JsonProperty property in contract.Properties)
310
      {
311
        try
312
        {
313
 468871
          if (!property.Ignored && property.Readable && ShouldSerialize(property, value))
314
          {
315
 468823
            object memberValue = property.ValueProvider.GetValue(value);
316
 468818
            JsonContract memberContract = GetContractSafe(memberValue);
317

  
318
 468818
            WriteMemberInfoProperty(writer, memberValue, property, memberContract);
319
          }
320
        }
321
 6
        catch (Exception ex)
322
        {
323
 6
          if (IsErrorHandled(value, contract, property.PropertyName, ex))
324
 3
            HandleError(writer, initialDepth);
325
          else
326
 3
            throw;
327
        }
328
      }
329

  
330
 142248
      writer.WriteEndObject();
331
 142248
      SerializeStack.RemoveAt(SerializeStack.Count - 1);
332

  
333
 142248
      contract.InvokeOnSerialized(value, Serializer.Context);
334
 142248
    }
335

  
336
    private void WriteTypeProperty(JsonWriter writer, Type type)
337
    {
338
 11
      writer.WritePropertyName(JsonTypeReflector.TypePropertyName);
339
 11
      writer.WriteValue(ReflectionUtils.GetTypeName(type, Serializer.TypeNameAssemblyFormat));
340
 11
    }
341

  
342
    private bool HasFlag(PreserveReferencesHandling value, PreserveReferencesHandling flag)
343
    {
344
 331290
      return ((value & flag) == flag);
345
 331290
    }
346

  
347
    private bool HasFlag(TypeNameHandling value, TypeNameHandling flag)
348
    {
349
 173585
      return ((value & flag) == flag);
350
 173585
    }
351

  
352
    private void SerializeConvertable(JsonWriter writer, JsonConverter converter, object value, JsonContract contract)
353
    {
354
 131
      if (ShouldWriteReference(value, null, contract))
355
      {
356
 1
        WriteReference(writer, value);
357
      }
358
      else
359
      {
360
 130
        if (!CheckForCircularReference(value, null, contract))
361
 0
          return;
362

  
363
 130
        SerializeStack.Add(value);
364

  
365
 130
        converter.WriteJson(writer, value, GetInternalSerializer());
366

  
367
 130
        SerializeStack.RemoveAt(SerializeStack.Count - 1);
368
      }
369
 131
    }
370

  
371
    private void SerializeList(JsonWriter writer, IList values, JsonArrayContract contract, JsonProperty member)
372
    {
373
 20908
      contract.InvokeOnSerializing(values, Serializer.Context);
374

  
375
 20908
      SerializeStack.Add(values);
376

  
377
 20908
      bool isReference = contract.IsReference ?? HasFlag(Serializer.PreserveReferencesHandling, PreserveReferencesHandling.Arrays);
378
 20908
      bool includeTypeDetails = HasFlag(((member != null) ? member.TypeNameHandling : null) ?? Serializer.TypeNameHandling, TypeNameHandling.Arrays);
379

  
380
 20908
      if (isReference || includeTypeDetails)
381
      {
382
 7
        writer.WriteStartObject();
383

  
384
 7
        if (isReference)
385
        {
386
 4
          writer.WritePropertyName(JsonTypeReflector.IdPropertyName);
387
 4
          writer.WriteValue(Serializer.ReferenceResolver.GetReference(values));
388
        }
389
 7
        if (includeTypeDetails)
390
        {
391
 3
          WriteTypeProperty(writer, values.GetType());
392
        }
393
 7
        writer.WritePropertyName(JsonTypeReflector.ArrayValuesPropertyName);
394
      }
395

  
396
 20908
      writer.WriteStartArray();
397

  
398
 20908
      int initialDepth = writer.Top;
399

  
400
 20908
      for (int i = 0; i < values.Count; i++)
401
      {
402
        try
403
        {
404
 52307
          object value = values[i];
405
 52305
          JsonContract valueContract = GetContractSafe(value);
406

  
407
 52305
          if (ShouldWriteReference(value, null, valueContract))
408
          {
409
 4
            WriteReference(writer, value);
410
          }
411
          else
412
          {
413
 52301
            if (!CheckForCircularReference(value, null, contract))
414
 1
              continue;
415

  
416
 52299
            SerializeValue(writer, value, null, valueContract);
417
          }
418
        }
419
 6
        catch (Exception ex)
420
        {
421
 6
          if (IsErrorHandled(values, contract, i, ex))
422
 3
            HandleError(writer, initialDepth);
423
          else
424
 3
            throw;
425
        }
426
      }
427

  
428
 20905
      writer.WriteEndArray();
429

  
430
 20905
      if (isReference || includeTypeDetails)
431
      {
432
 7
        writer.WriteEndObject();
433
      }
434

  
435
 20905
      SerializeStack.RemoveAt(SerializeStack.Count - 1);
436

  
437
 20905
      contract.InvokeOnSerialized(values, Serializer.Context);
438
 20905
    }
439

  
440
#if !SILVERLIGHT && !PocketPC
441
    private void SerializeISerializable(JsonWriter writer, ISerializable value, JsonISerializableContract contract)
442
    {
443
 2
      contract.InvokeOnSerializing(value, Serializer.Context);
444
 2
      SerializeStack.Add(value);
445

  
446
 2
      writer.WriteStartObject();
447

  
448
 2
      SerializationInfo serializationInfo = new SerializationInfo(contract.UnderlyingType, new FormatterConverter());
449
 2
      value.GetObjectData(serializationInfo, Serializer.Context);
450

  
451
 2
      foreach (SerializationEntry serializationEntry in serializationInfo)
452
      {
453
 29
        writer.WritePropertyName(serializationEntry.Name);
454
 29
        SerializeValue(writer, serializationEntry.Value, null, GetContractSafe(serializationEntry.Value));
455
      }
456

  
457
 2
      writer.WriteEndObject();
458

  
459
 2
      SerializeStack.RemoveAt(SerializeStack.Count - 1);
460
 2
      contract.InvokeOnSerialized(value, Serializer.Context);
461
 2
    }
462
#endif
463

  
464
    private void SerializeDictionary(JsonWriter writer, IWrappedDictionary values, JsonDictionaryContract contract, JsonProperty member)
465
    {
466
 10426
      contract.InvokeOnSerializing(values.UnderlyingDictionary, Serializer.Context);
467

  
468
 10426
      SerializeStack.Add(values.UnderlyingDictionary);
469
 10426
      writer.WriteStartObject();
470

  
471
 10426
      bool isReference = contract.IsReference ?? HasFlag(Serializer.PreserveReferencesHandling, PreserveReferencesHandling.Objects);
472
 10426
      if (isReference)
473
      {
474
 4
        writer.WritePropertyName(JsonTypeReflector.IdPropertyName);
475
 4
        writer.WriteValue(Serializer.ReferenceResolver.GetReference(values.UnderlyingDictionary));
476
      }
477
 10426
      if (HasFlag(((member != null) ? member.TypeNameHandling : null) ?? Serializer.TypeNameHandling, TypeNameHandling.Objects))
478
      {
479
 0
        WriteTypeProperty(writer, values.UnderlyingDictionary.GetType());
480
      }
481

  
482
 10426
      int initialDepth = writer.Top;
483

  
484
 10426
      foreach (DictionaryEntry entry in values)
485
      {
486
 31255
        string propertyName = GetPropertyName(entry);
487

  
488
        try
489
        {
490
 31255
          object value = entry.Value;
491
 31255
          JsonContract valueContract = GetContractSafe(value);
492

  
493
 31255
          if (ShouldWriteReference(value, null, valueContract))
494
          {
495
 4
            writer.WritePropertyName(propertyName);
496
 4
            WriteReference(writer, value);
497
          }
498
          else
499
          {
500
 31251
            if (!CheckForCircularReference(value, null, contract))
501
 1
              continue;
502

  
503
 31249
            writer.WritePropertyName(propertyName);
504

  
505
 31249
            SerializeValue(writer, value, null, valueContract);
506
          }
507
        }
508
 1
        catch (Exception ex)
509
        {
510
 1
          if (IsErrorHandled(values.UnderlyingDictionary, contract, propertyName, ex))
511
 0
            HandleError(writer, initialDepth);
512
          else
513
 1
            throw;
514
        }
515
      }
516

  
517
 10425
      writer.WriteEndObject();
518
 10425
      SerializeStack.RemoveAt(SerializeStack.Count - 1);
519

  
520
 10425
      contract.InvokeOnSerialized(values.UnderlyingDictionary, Serializer.Context);
521
 10425
    }
522

  
523
    private string GetPropertyName(DictionaryEntry entry)
524
    {
525
      string propertyName;
526

  
527
 31255
      if (entry.Key is IConvertible)
528
 31249
        return Convert.ToString(entry.Key, CultureInfo.InvariantCulture);
529
 6
      else if (TryConvertToString(entry.Key, entry.Key.GetType(), out propertyName))
530
 2
        return propertyName;
531
      else
532
 4
        return entry.Key.ToString();
533
 31255
    }
534

  
535
    private void HandleError(JsonWriter writer, int initialDepth)
536
    {
537
 6
      ClearErrorContext();
538

  
539
 7
      while (writer.Top > initialDepth)
540
      {
541
 1
        writer.WriteEnd();
542
      }
543
 6
    }
544

  
545
    private bool ShouldSerialize(JsonProperty property, object target)
546
    {
547
 468826
      if (property.ShouldSerialize == null)
548
 468821
        return true;
549

  
550
 5
      return property.ShouldSerialize(target);
551
 468826
    }
552
  }
553
}