Click here to Skip to main content
15,886,199 members
Articles / General Programming / Algorithms

Jazz Up Your C# Code

Rate me:
Please Sign up or sign in to vote.
4.95/5 (11 votes)
3 Jul 2012CPOL28 min read 51.5K   369   54  
Maintaining code with complex permissions tends to be difficult, because the code can be distributed across multiple classes. By embedding permissions directly on methods and properties within a class, code is reduced and maintainability is simplified.
#region License
// Copyright (c) 2007 James Newton-King
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
#endregion

using System;
using System.Collections.Generic;
using System.IO;
using Newtonsoft.Json.Utilities;
using System.Globalization;
#if NETFX_CORE
using IConvertible = Newtonsoft.Json.Utilities.Convertible;
#endif
#if NET20
using Newtonsoft.Json.Utilities.LinqBridge;
#else
using System.Linq;
#endif

namespace Newtonsoft.Json
{
  /// <summary>
  /// Represents a writer that provides a fast, non-cached, forward-only way of generating Json data.
  /// </summary>
  public abstract class JsonWriter : IDisposable
  {
    internal enum State
    {
      Start,
      Property,
      ObjectStart,
      Object,
      ArrayStart,
      Array,
      ConstructorStart,
      Constructor,
      Bytes,
      Closed,
      Error
    }

    // array that gives a new state based on the current state an the token being written
    private static readonly State[][] StateArray;

    internal static readonly State[][] StateArrayTempate = new[] {
//                                      Start                   PropertyName            ObjectStart         Object            ArrayStart              Array                   ConstructorStart        Constructor             Closed          Error
//                        
/* None                        */new[]{ State.Error,            State.Error,            State.Error,        State.Error,      State.Error,            State.Error,            State.Error,            State.Error,            State.Error,    State.Error },
/* StartObject                 */new[]{ State.ObjectStart,      State.ObjectStart,      State.Error,        State.Error,      State.ObjectStart,      State.ObjectStart,      State.ObjectStart,      State.ObjectStart,      State.Error,    State.Error },
/* StartArray                  */new[]{ State.ArrayStart,       State.ArrayStart,       State.Error,        State.Error,      State.ArrayStart,       State.ArrayStart,       State.ArrayStart,       State.ArrayStart,       State.Error,    State.Error },
/* StartConstructor            */new[]{ State.ConstructorStart, State.ConstructorStart, State.Error,        State.Error,      State.ConstructorStart, State.ConstructorStart, State.ConstructorStart, State.ConstructorStart, State.Error,    State.Error },
/* StartProperty               */new[]{ State.Property,         State.Error,            State.Property,     State.Property,   State.Error,            State.Error,            State.Error,            State.Error,            State.Error,    State.Error },
/* Comment                     */new[]{ State.Start,            State.Property,         State.ObjectStart,  State.Object,     State.ArrayStart,       State.Array,            State.Constructor,      State.Constructor,      State.Error,    State.Error },
/* Raw                         */new[]{ State.Start,            State.Property,         State.ObjectStart,  State.Object,     State.ArrayStart,       State.Array,            State.Constructor,      State.Constructor,      State.Error,    State.Error },
/* Value (this will be copied) */new[]{ State.Start,            State.Object,           State.Error,        State.Error,      State.Array,            State.Array,            State.Constructor,      State.Constructor,      State.Error,    State.Error }
		};

    internal static State[][] BuildStateArray()
    {
      var allStates = StateArrayTempate.ToList();
      var errorStates = StateArrayTempate[0];
      var valueStates = StateArrayTempate[7];

      foreach (JsonToken valueToken in EnumUtils.GetValues(typeof(JsonToken)))
      {
        if (allStates.Count <= (int)valueToken)
        {
          switch (valueToken)
          {
            case JsonToken.Integer:
            case JsonToken.Float:
            case JsonToken.String:
            case JsonToken.Boolean:
            case JsonToken.Null:
            case JsonToken.Undefined:
            case JsonToken.Date:
            case JsonToken.Bytes:
              allStates.Add(valueStates);
              break;
            default:
              allStates.Add(errorStates);
              break;
          }
        }
      }

      return allStates.ToArray();
    }

    static JsonWriter()
    {
      StateArray = BuildStateArray();
    }

    private readonly List<JsonPosition> _stack;
    private JsonPosition _currentPosition;
    private State _currentState;
    private Formatting _formatting;

    /// <summary>
    /// Gets or sets a value indicating whether the underlying stream or
    /// <see cref="TextReader"/> should be closed when the writer is closed.
    /// </summary>
    /// <value>
    /// true to close the underlying stream or <see cref="TextReader"/> when
    /// the writer is closed; otherwise false. The default is true.
    /// </value>
    public bool CloseOutput { get; set; }

    /// <summary>
    /// Gets the top.
    /// </summary>
    /// <value>The top.</value>
    protected internal int Top
    {
      get
      {
        int depth = _stack.Count;
        if (Peek() != JsonContainerType.None)
          depth++;

        return depth;
      }
    }

    internal string ContainerPath
    {
      get
      {
        if (_currentPosition.Type == JsonContainerType.None)
          return string.Empty;

        IEnumerable<JsonPosition> positions = (_currentPosition.InsideContainer())
          ? _stack
          : _stack.Concat(new[] { _currentPosition });

        return JsonPosition.BuildPath(positions);
      }
    }
    
    /// <summary>
    /// Gets the state of the writer.
    /// </summary>
    public WriteState WriteState
    {
      get
      {
        switch (_currentState)
        {
          case State.Error:
            return WriteState.Error;
          case State.Closed:
            return WriteState.Closed;
          case State.Object:
          case State.ObjectStart:
            return WriteState.Object;
          case State.Array:
          case State.ArrayStart:
            return WriteState.Array;
          case State.Constructor:
          case State.ConstructorStart:
            return WriteState.Constructor;
          case State.Property:
            return WriteState.Property;
          case State.Start:
            return WriteState.Start;
          default:
            throw new JsonWriterException("Invalid state: " + _currentState);
        }
      }
    }

    /// <summary>
    /// Gets the path of the writer. 
    /// </summary>
    public string Path
    {
      get
      {
        if (_currentPosition.Type == JsonContainerType.None)
          return string.Empty;

        return JsonPosition.BuildPath(_stack.Concat(new[] { _currentPosition }));
      }
    }

    private DateFormatHandling _dateFormatHandling;
    private DateTimeZoneHandling _dateTimeZoneHandling;

    /// <summary>
    /// Indicates how JSON text output is formatted.
    /// </summary>
    public Formatting Formatting
    {
      get { return _formatting; }
      set { _formatting = value; }
    }

    /// <summary>
    /// Get or set how dates are written to JSON text.
    /// </summary>
    public DateFormatHandling DateFormatHandling
    {
      get { return _dateFormatHandling; }
      set { _dateFormatHandling = value; }
    }

    /// <summary>
    /// Get or set how <see cref="DateTime"/> time zones are handling when writing JSON.
    /// </summary>
    public DateTimeZoneHandling DateTimeZoneHandling
    {
      get { return _dateTimeZoneHandling; }
      set { _dateTimeZoneHandling = value; }
    }

    /// <summary>
    /// Creates an instance of the <c>JsonWriter</c> class. 
    /// </summary>
    protected JsonWriter()
    {
      _stack = new List<JsonPosition>(4);
      _currentState = State.Start;
      _formatting = Formatting.None;
      _dateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind;

      CloseOutput = true;
    }

    private void UpdateScopeWithFinishedValue()
    {
      if (_currentPosition.Type == JsonContainerType.Array
        || _currentPosition.Type == JsonContainerType.Constructor)
      {
        if (_currentPosition.Position == null)
          _currentPosition.Position = 0;
        else
          _currentPosition.Position++;
      }
    }

    private void Push(JsonContainerType value)
    {
      UpdateScopeWithFinishedValue();

      if (_currentPosition.Type == JsonContainerType.None)
      {
        _currentPosition.Type = value;
      }
      else
      {
        _stack.Add(_currentPosition);
        var state = new JsonPosition
        {
          Type = value
        };
        _currentPosition = state;
      }
    }

    private JsonContainerType Pop()
    {
      JsonPosition oldPosition;
      if (_stack.Count > 0)
      {
        oldPosition = _currentPosition;
        _currentPosition = _stack[_stack.Count - 1];
        _stack.RemoveAt(_stack.Count - 1);
      }
      else
      {
        oldPosition = _currentPosition;
        _currentPosition = new JsonPosition();
      }

      return oldPosition.Type;
    }

    private JsonContainerType Peek()
    {
      return _currentPosition.Type;
    }

    /// <summary>
    /// Flushes whatever is in the buffer to the underlying streams and also flushes the underlying stream.
    /// </summary>
    public abstract void Flush();

    /// <summary>
    /// Closes this stream and the underlying stream.
    /// </summary>
    public virtual void Close()
    {
      AutoCompleteAll();
    }

    /// <summary>
    /// Writes the beginning of a Json object.
    /// </summary>
    public virtual void WriteStartObject()
    {
      AutoComplete(JsonToken.StartObject);
      Push(JsonContainerType.Object);
    }

    /// <summary>
    /// Writes the end of a Json object.
    /// </summary>
    public virtual void WriteEndObject()
    {
      AutoCompleteClose(JsonToken.EndObject);
    }

    /// <summary>
    /// Writes the beginning of a Json array.
    /// </summary>
    public virtual void WriteStartArray()
    {
      AutoComplete(JsonToken.StartArray);
      Push(JsonContainerType.Array);
    }

    /// <summary>
    /// Writes the end of an array.
    /// </summary>
    public virtual void WriteEndArray()
    {
      AutoCompleteClose(JsonToken.EndArray);
    }

    /// <summary>
    /// Writes the start of a constructor with the given name.
    /// </summary>
    /// <param name="name">The name of the constructor.</param>
    public virtual void WriteStartConstructor(string name)
    {
      AutoComplete(JsonToken.StartConstructor);
      Push(JsonContainerType.Constructor);
    }

    /// <summary>
    /// Writes the end constructor.
    /// </summary>
    public virtual void WriteEndConstructor()
    {
      AutoCompleteClose(JsonToken.EndConstructor);
    }

    /// <summary>
    /// Writes the property name of a name/value pair on a Json object.
    /// </summary>
    /// <param name="name">The name of the property.</param>
    public virtual void WritePropertyName(string name)
    {
      _currentPosition.PropertyName = name;
      AutoComplete(JsonToken.PropertyName);
    }

    /// <summary>
    /// Writes the end of the current Json object or array.
    /// </summary>
    public virtual void WriteEnd()
    {
      WriteEnd(Peek());
    }

    /// <summary>
    /// Writes the current <see cref="JsonReader"/> token.
    /// </summary>
    /// <param name="reader">The <see cref="JsonReader"/> to read the token from.</param>
    public void WriteToken(JsonReader reader)
    {
      ValidationUtils.ArgumentNotNull(reader, "reader");

      int initialDepth;

      if (reader.TokenType == JsonToken.None)
        initialDepth = -1;
      else if (!IsStartToken(reader.TokenType))
        initialDepth = reader.Depth + 1;
      else
        initialDepth = reader.Depth;

      WriteToken(reader, initialDepth);
    }

    internal void WriteToken(JsonReader reader, int initialDepth)
    {
      do
      {
        switch (reader.TokenType)
        {
          case JsonToken.None:
            // read to next
            break;
          case JsonToken.StartObject:
            WriteStartObject();
            break;
          case JsonToken.StartArray:
            WriteStartArray();
            break;
          case JsonToken.StartConstructor:
            string constructorName = reader.Value.ToString();
            // write a JValue date when the constructor is for a date
            if (string.Equals(constructorName, "Date", StringComparison.Ordinal))
              WriteConstructorDate(reader);
            else
              WriteStartConstructor(reader.Value.ToString());
            break;
          case JsonToken.PropertyName:
            WritePropertyName(reader.Value.ToString());
            break;
          case JsonToken.Comment:
            WriteComment(reader.Value.ToString());
            break;
          case JsonToken.Integer:
            WriteValue(Convert.ToInt64(reader.Value, CultureInfo.InvariantCulture));
            break;
          case JsonToken.Float:
            WriteValue(Convert.ToDouble(reader.Value, CultureInfo.InvariantCulture));
            break;
          case JsonToken.String:
            WriteValue(reader.Value.ToString());
            break;
          case JsonToken.Boolean:
            WriteValue(Convert.ToBoolean(reader.Value, CultureInfo.InvariantCulture));
            break;
          case JsonToken.Null:
            WriteNull();
            break;
          case JsonToken.Undefined:
            WriteUndefined();
            break;
          case JsonToken.EndObject:
            WriteEndObject();
            break;
          case JsonToken.EndArray:
            WriteEndArray();
            break;
          case JsonToken.EndConstructor:
            WriteEndConstructor();
            break;
          case JsonToken.Date:
            WriteValue((DateTime)reader.Value);
            break;
          case JsonToken.Raw:
            WriteRawValue((string)reader.Value);
            break;
          case JsonToken.Bytes:
            WriteValue((byte[])reader.Value);
            break;
          default:
            throw MiscellaneousUtils.CreateArgumentOutOfRangeException("TokenType", reader.TokenType, "Unexpected token type.");
        }
      }
      while (
        // stop if we have reached the end of the token being read
        initialDepth - 1 < reader.Depth - (IsEndToken(reader.TokenType) ? 1 : 0)
        && reader.Read());
    }

    private void WriteConstructorDate(JsonReader reader)
    {
      if (!reader.Read())
        throw new JsonWriterException("Unexpected end when reading date constructor.");
      if (reader.TokenType != JsonToken.Integer)
        throw new JsonWriterException("Unexpected token when reading date constructor. Expected Integer, got " + reader.TokenType);

      long ticks = (long)reader.Value;
      DateTime date = JsonConvert.ConvertJavaScriptTicksToDateTime(ticks);

      if (!reader.Read())
        throw new JsonWriterException("Unexpected end when reading date constructor.");
      if (reader.TokenType != JsonToken.EndConstructor)
        throw new JsonWriterException("Unexpected token when reading date constructor. Expected EndConstructor, got " + reader.TokenType);

      WriteValue(date);
    }

    private bool IsEndToken(JsonToken token)
    {
      switch (token)
      {
        case JsonToken.EndObject:
        case JsonToken.EndArray:
        case JsonToken.EndConstructor:
          return true;
        default:
          return false;
      }
    }

    private bool IsStartToken(JsonToken token)
    {
      switch (token)
      {
        case JsonToken.StartObject:
        case JsonToken.StartArray:
        case JsonToken.StartConstructor:
          return true;
        default:
          return false;
      }
    }

    private void WriteEnd(JsonContainerType type)
    {
      switch (type)
      {
        case JsonContainerType.Object:
          WriteEndObject();
          break;
        case JsonContainerType.Array:
          WriteEndArray();
          break;
        case JsonContainerType.Constructor:
          WriteEndConstructor();
          break;
        default:
          throw new JsonWriterException("Unexpected type when writing end: " + type);
      }
    }

    private void AutoCompleteAll()
    {
      while (Top > 0)
      {
        WriteEnd();
      }
    }

    private JsonContainerType GetTypeForCloseToken(JsonToken token)
    {
      switch (token)
      {
        case JsonToken.EndObject:
          return JsonContainerType.Object;
        case JsonToken.EndArray:
          return JsonContainerType.Array;
        case JsonToken.EndConstructor:
          return JsonContainerType.Constructor;
        default:
          throw new JsonWriterException("No type for token: " + token);
      }
    }

    private JsonToken GetCloseTokenForType(JsonContainerType type)
    {
      switch (type)
      {
        case JsonContainerType.Object:
          return JsonToken.EndObject;
        case JsonContainerType.Array:
          return JsonToken.EndArray;
        case JsonContainerType.Constructor:
          return JsonToken.EndConstructor;
        default:
          throw new JsonWriterException("No close token for type: " + type);
      }
    }

    private void AutoCompleteClose(JsonToken tokenBeingClosed)
    {
      // write closing symbol and calculate new state

      int levelsToComplete = 0;
      JsonContainerType type = GetTypeForCloseToken(tokenBeingClosed);

      if (_currentPosition.Type == type)
      {
        levelsToComplete = 1;
      }
      else
      {
        int top = Top - 2;
        for (int i = top; i >= 0; i--)
        {
          int currentLevel = top - i;

          if (_stack[currentLevel].Type == type)
          {
            levelsToComplete = i + 2;
            break;
          }
        }
      }

      if (levelsToComplete == 0)
        throw new JsonWriterException("No token to close.");

      for (int i = 0; i < levelsToComplete; i++)
      {
        JsonToken token = GetCloseTokenForType(Pop());

        if (_formatting == Formatting.Indented)
        {
          if (_currentState != State.ObjectStart && _currentState != State.ArrayStart)
            WriteIndent();
        }

        WriteEnd(token);

        JsonContainerType currentLevelType = Peek();

        switch (currentLevelType)
        {
          case JsonContainerType.Object:
            _currentState = State.Object;
            break;
          case JsonContainerType.Array:
            _currentState = State.Array;
            break;
          case JsonContainerType.Constructor:
            _currentState = State.Array;
            break;
          case JsonContainerType.None:
            _currentState = State.Start;
            break;
          default:
            throw new JsonWriterException("Unknown JsonType: " + currentLevelType);
        }
      }
    }

    /// <summary>
    /// Writes the specified end token.
    /// </summary>
    /// <param name="token">The end token to write.</param>
    protected virtual void WriteEnd(JsonToken token)
    {
    }

    /// <summary>
    /// Writes indent characters.
    /// </summary>
    protected virtual void WriteIndent()
    {
    }

    /// <summary>
    /// Writes the JSON value delimiter.
    /// </summary>
    protected virtual void WriteValueDelimiter()
    {
    }

    /// <summary>
    /// Writes an indent space.
    /// </summary>
    protected virtual void WriteIndentSpace()
    {
    }

    internal void AutoComplete(JsonToken tokenBeingWritten)
    {
      if (tokenBeingWritten != JsonToken.StartObject
        && tokenBeingWritten != JsonToken.StartArray
        && tokenBeingWritten != JsonToken.StartConstructor)
        UpdateScopeWithFinishedValue();

      // gets new state based on the current state and what is being written
      State newState = StateArray[(int)tokenBeingWritten][(int)_currentState];

      if (newState == State.Error)
      {
        throw new JsonWriterException("Token {0} in state {1} would result in an invalid JSON object.".FormatWith(CultureInfo.InvariantCulture, tokenBeingWritten.ToString(), _currentState.ToString()));
      }

      if ((_currentState == State.Object || _currentState == State.Array || _currentState == State.Constructor) && tokenBeingWritten != JsonToken.Comment)
      {
        WriteValueDelimiter();
      }
      else if (_currentState == State.Property)
      {
        if (_formatting == Formatting.Indented)
          WriteIndentSpace();
      }

      if (_formatting == Formatting.Indented)
      {
        WriteState writeState = WriteState;

        // don't indent a property when it is the first token to be written (i.e. at the start)
        if ((tokenBeingWritten == JsonToken.PropertyName && writeState != WriteState.Start) ||
            writeState == WriteState.Array || writeState == WriteState.Constructor)
        {
          WriteIndent();
        }
      }

      _currentState = newState;
    }

    #region WriteValue methods
    /// <summary>
    /// Writes a null value.
    /// </summary>
    public virtual void WriteNull()
    {
      AutoComplete(JsonToken.Null);
    }

    /// <summary>
    /// Writes an undefined value.
    /// </summary>
    public virtual void WriteUndefined()
    {
      AutoComplete(JsonToken.Undefined);
    }

    /// <summary>
    /// Writes raw JSON without changing the writer's state.
    /// </summary>
    /// <param name="json">The raw JSON to write.</param>
    public virtual void WriteRaw(string json)
    {
    }

    /// <summary>
    /// Writes raw JSON where a value is expected and updates the writer's state.
    /// </summary>
    /// <param name="json">The raw JSON to write.</param>
    public virtual void WriteRawValue(string json)
    {
      // hack. want writer to change state as if a value had been written
      AutoComplete(JsonToken.Undefined);
      WriteRaw(json);
    }

    /// <summary>
    /// Writes a <see cref="String"/> value.
    /// </summary>
    /// <param name="value">The <see cref="String"/> value to write.</param>
    public virtual void WriteValue(string value)
    {
      AutoComplete(JsonToken.String);
    }

    /// <summary>
    /// Writes a <see cref="Int32"/> value.
    /// </summary>
    /// <param name="value">The <see cref="Int32"/> value to write.</param>
    public virtual void WriteValue(int value)
    {
      AutoComplete(JsonToken.Integer);
    }

    /// <summary>
    /// Writes a <see cref="UInt32"/> value.
    /// </summary>
    /// <param name="value">The <see cref="UInt32"/> value to write.</param>
    [CLSCompliant(false)]
    public virtual void WriteValue(uint value)
    {
      AutoComplete(JsonToken.Integer);
    }

    /// <summary>
    /// Writes a <see cref="Int64"/> value.
    /// </summary>
    /// <param name="value">The <see cref="Int64"/> value to write.</param>
    public virtual void WriteValue(long value)
    {
      AutoComplete(JsonToken.Integer);
    }

    /// <summary>
    /// Writes a <see cref="UInt64"/> value.
    /// </summary>
    /// <param name="value">The <see cref="UInt64"/> value to write.</param>
    [CLSCompliant(false)]
    public virtual void WriteValue(ulong value)
    {
      AutoComplete(JsonToken.Integer);
    }

    /// <summary>
    /// Writes a <see cref="Single"/> value.
    /// </summary>
    /// <param name="value">The <see cref="Single"/> value to write.</param>
    public virtual void WriteValue(float value)
    {
      AutoComplete(JsonToken.Float);
    }

    /// <summary>
    /// Writes a <see cref="Double"/> value.
    /// </summary>
    /// <param name="value">The <see cref="Double"/> value to write.</param>
    public virtual void WriteValue(double value)
    {
      AutoComplete(JsonToken.Float);
    }

    /// <summary>
    /// Writes a <see cref="Boolean"/> value.
    /// </summary>
    /// <param name="value">The <see cref="Boolean"/> value to write.</param>
    public virtual void WriteValue(bool value)
    {
      AutoComplete(JsonToken.Boolean);
    }

    /// <summary>
    /// Writes a <see cref="Int16"/> value.
    /// </summary>
    /// <param name="value">The <see cref="Int16"/> value to write.</param>
    public virtual void WriteValue(short value)
    {
      AutoComplete(JsonToken.Integer);
    }

    /// <summary>
    /// Writes a <see cref="UInt16"/> value.
    /// </summary>
    /// <param name="value">The <see cref="UInt16"/> value to write.</param>
    [CLSCompliant(false)]
    public virtual void WriteValue(ushort value)
    {
      AutoComplete(JsonToken.Integer);
    }

    /// <summary>
    /// Writes a <see cref="Char"/> value.
    /// </summary>
    /// <param name="value">The <see cref="Char"/> value to write.</param>
    public virtual void WriteValue(char value)
    {
      AutoComplete(JsonToken.String);
    }

    /// <summary>
    /// Writes a <see cref="Byte"/> value.
    /// </summary>
    /// <param name="value">The <see cref="Byte"/> value to write.</param>
    public virtual void WriteValue(byte value)
    {
      AutoComplete(JsonToken.Integer);
    }

    /// <summary>
    /// Writes a <see cref="SByte"/> value.
    /// </summary>
    /// <param name="value">The <see cref="SByte"/> value to write.</param>
    [CLSCompliant(false)]
    public virtual void WriteValue(sbyte value)
    {
      AutoComplete(JsonToken.Integer);
    }

    /// <summary>
    /// Writes a <see cref="Decimal"/> value.
    /// </summary>
    /// <param name="value">The <see cref="Decimal"/> value to write.</param>
    public virtual void WriteValue(decimal value)
    {
      AutoComplete(JsonToken.Float);
    }

    /// <summary>
    /// Writes a <see cref="DateTime"/> value.
    /// </summary>
    /// <param name="value">The <see cref="DateTime"/> value to write.</param>
    public virtual void WriteValue(DateTime value)
    {
      AutoComplete(JsonToken.Date);
    }

#if !PocketPC && !NET20
    /// <summary>
    /// Writes a <see cref="DateTimeOffset"/> value.
    /// </summary>
    /// <param name="value">The <see cref="DateTimeOffset"/> value to write.</param>
    public virtual void WriteValue(DateTimeOffset value)
    {
      AutoComplete(JsonToken.Date);
    }
#endif

    /// <summary>
    /// Writes a <see cref="Guid"/> value.
    /// </summary>
    /// <param name="value">The <see cref="Guid"/> value to write.</param>
    public virtual void WriteValue(Guid value)
    {
      AutoComplete(JsonToken.String);
    }

    /// <summary>
    /// Writes a <see cref="TimeSpan"/> value.
    /// </summary>
    /// <param name="value">The <see cref="TimeSpan"/> value to write.</param>
    public virtual void WriteValue(TimeSpan value)
    {
      AutoComplete(JsonToken.String);
    }

    /// <summary>
    /// Writes a <see cref="Nullable{Int32}"/> value.
    /// </summary>
    /// <param name="value">The <see cref="Nullable{Int32}"/> value to write.</param>
    public virtual void WriteValue(int? value)
    {
      if (value == null)
        WriteNull();
      else
        WriteValue(value.Value);
    }

    /// <summary>
    /// Writes a <see cref="Nullable{UInt32}"/> value.
    /// </summary>
    /// <param name="value">The <see cref="Nullable{UInt32}"/> value to write.</param>
    [CLSCompliant(false)]
    public virtual void WriteValue(uint? value)
    {
      if (value == null)
        WriteNull();
      else
        WriteValue(value.Value);
    }

    /// <summary>
    /// Writes a <see cref="Nullable{Int64}"/> value.
    /// </summary>
    /// <param name="value">The <see cref="Nullable{Int64}"/> value to write.</param>
    public virtual void WriteValue(long? value)
    {
      if (value == null)
        WriteNull();
      else
        WriteValue(value.Value);
    }

    /// <summary>
    /// Writes a <see cref="Nullable{UInt64}"/> value.
    /// </summary>
    /// <param name="value">The <see cref="Nullable{UInt64}"/> value to write.</param>
    [CLSCompliant(false)]
    public virtual void WriteValue(ulong? value)
    {
      if (value == null)
        WriteNull();
      else
        WriteValue(value.Value);
    }

    /// <summary>
    /// Writes a <see cref="Nullable{Single}"/> value.
    /// </summary>
    /// <param name="value">The <see cref="Nullable{Single}"/> value to write.</param>
    public virtual void WriteValue(float? value)
    {
      if (value == null)
        WriteNull();
      else
        WriteValue(value.Value);
    }

    /// <summary>
    /// Writes a <see cref="Nullable{Double}"/> value.
    /// </summary>
    /// <param name="value">The <see cref="Nullable{Double}"/> value to write.</param>
    public virtual void WriteValue(double? value)
    {
      if (value == null)
        WriteNull();
      else
        WriteValue(value.Value);
    }

    /// <summary>
    /// Writes a <see cref="Nullable{Boolean}"/> value.
    /// </summary>
    /// <param name="value">The <see cref="Nullable{Boolean}"/> value to write.</param>
    public virtual void WriteValue(bool? value)
    {
      if (value == null)
        WriteNull();
      else
        WriteValue(value.Value);
    }

    /// <summary>
    /// Writes a <see cref="Nullable{Int16}"/> value.
    /// </summary>
    /// <param name="value">The <see cref="Nullable{Int16}"/> value to write.</param>
    public virtual void WriteValue(short? value)
    {
      if (value == null)
        WriteNull();
      else
        WriteValue(value.Value);
    }

    /// <summary>
    /// Writes a <see cref="Nullable{UInt16}"/> value.
    /// </summary>
    /// <param name="value">The <see cref="Nullable{UInt16}"/> value to write.</param>
    [CLSCompliant(false)]
    public virtual void WriteValue(ushort? value)
    {
      if (value == null)
        WriteNull();
      else
        WriteValue(value.Value);
    }

    /// <summary>
    /// Writes a <see cref="Nullable{Char}"/> value.
    /// </summary>
    /// <param name="value">The <see cref="Nullable{Char}"/> value to write.</param>
    public virtual void WriteValue(char? value)
    {
      if (value == null)
        WriteNull();
      else
        WriteValue(value.Value);
    }

    /// <summary>
    /// Writes a <see cref="Nullable{Byte}"/> value.
    /// </summary>
    /// <param name="value">The <see cref="Nullable{Byte}"/> value to write.</param>
    public virtual void WriteValue(byte? value)
    {
      if (value == null)
        WriteNull();
      else
        WriteValue(value.Value);
    }

    /// <summary>
    /// Writes a <see cref="Nullable{SByte}"/> value.
    /// </summary>
    /// <param name="value">The <see cref="Nullable{SByte}"/> value to write.</param>
    [CLSCompliant(false)]
    public virtual void WriteValue(sbyte? value)
    {
      if (value == null)
        WriteNull();
      else
        WriteValue(value.Value);
    }

    /// <summary>
    /// Writes a <see cref="Nullable{Decimal}"/> value.
    /// </summary>
    /// <param name="value">The <see cref="Nullable{Decimal}"/> value to write.</param>
    public virtual void WriteValue(decimal? value)
    {
      if (value == null)
        WriteNull();
      else
        WriteValue(value.Value);
    }

    /// <summary>
    /// Writes a <see cref="Nullable{DateTime}"/> value.
    /// </summary>
    /// <param name="value">The <see cref="Nullable{DateTime}"/> value to write.</param>
    public virtual void WriteValue(DateTime? value)
    {
      if (value == null)
        WriteNull();
      else
        WriteValue(value.Value);
    }

#if !PocketPC && !NET20
    /// <summary>
    /// Writes a <see cref="Nullable{DateTimeOffset}"/> value.
    /// </summary>
    /// <param name="value">The <see cref="Nullable{DateTimeOffset}"/> value to write.</param>
    public virtual void WriteValue(DateTimeOffset? value)
    {
      if (value == null)
        WriteNull();
      else
        WriteValue(value.Value);
    }
#endif

    /// <summary>
    /// Writes a <see cref="Nullable{Guid}"/> value.
    /// </summary>
    /// <param name="value">The <see cref="Nullable{Guid}"/> value to write.</param>
    public virtual void WriteValue(Guid? value)
    {
      if (value == null)
        WriteNull();
      else
        WriteValue(value.Value);
    }

    /// <summary>
    /// Writes a <see cref="Nullable{TimeSpan}"/> value.
    /// </summary>
    /// <param name="value">The <see cref="Nullable{TimeSpan}"/> value to write.</param>
    public virtual void WriteValue(TimeSpan? value)
    {
      if (value == null)
        WriteNull();
      else
        WriteValue(value.Value);
    }

    /// <summary>
    /// Writes a <see cref="T:Byte[]"/> value.
    /// </summary>
    /// <param name="value">The <see cref="T:Byte[]"/> value to write.</param>
    public virtual void WriteValue(byte[] value)
    {
      if (value == null)
        WriteNull();
      else
        AutoComplete(JsonToken.Bytes);
    }

    /// <summary>
    /// Writes a <see cref="Uri"/> value.
    /// </summary>
    /// <param name="value">The <see cref="Uri"/> value to write.</param>
    public virtual void WriteValue(Uri value)
    {
      if (value == null)
        WriteNull();
      else
        AutoComplete(JsonToken.String);
    }

    /// <summary>
    /// Writes a <see cref="Object"/> value.
    /// An error will raised if the value cannot be written as a single JSON token.
    /// </summary>
    /// <param name="value">The <see cref="Object"/> value to write.</param>
    public virtual void WriteValue(object value)
    {
      if (value == null)
      {
        WriteNull();
        return;
      }
      else if (ConvertUtils.IsConvertible(value))
      {
        IConvertible convertible = ConvertUtils.ToConvertible(value);

        switch (convertible.GetTypeCode())
        {
          case TypeCode.String:
            WriteValue(convertible.ToString(CultureInfo.InvariantCulture));
            return;
          case TypeCode.Char:
            WriteValue(convertible.ToChar(CultureInfo.InvariantCulture));
            return;
          case TypeCode.Boolean:
            WriteValue(convertible.ToBoolean(CultureInfo.InvariantCulture));
            return;
          case TypeCode.SByte:
            WriteValue(convertible.ToSByte(CultureInfo.InvariantCulture));
            return;
          case TypeCode.Int16:
            WriteValue(convertible.ToInt16(CultureInfo.InvariantCulture));
            return;
          case TypeCode.UInt16:
            WriteValue(convertible.ToUInt16(CultureInfo.InvariantCulture));
            return;
          case TypeCode.Int32:
            WriteValue(convertible.ToInt32(CultureInfo.InvariantCulture));
            return;
          case TypeCode.Byte:
            WriteValue(convertible.ToByte(CultureInfo.InvariantCulture));
            return;
          case TypeCode.UInt32:
            WriteValue(convertible.ToUInt32(CultureInfo.InvariantCulture));
            return;
          case TypeCode.Int64:
            WriteValue(convertible.ToInt64(CultureInfo.InvariantCulture));
            return;
          case TypeCode.UInt64:
            WriteValue(convertible.ToUInt64(CultureInfo.InvariantCulture));
            return;
          case TypeCode.Single:
            WriteValue(convertible.ToSingle(CultureInfo.InvariantCulture));
            return;
          case TypeCode.Double:
            WriteValue(convertible.ToDouble(CultureInfo.InvariantCulture));
            return;
          case TypeCode.DateTime:
            WriteValue(convertible.ToDateTime(CultureInfo.InvariantCulture));
            return;
          case TypeCode.Decimal:
            WriteValue(convertible.ToDecimal(CultureInfo.InvariantCulture));
            return;
#if !NETFX_CORE
          case TypeCode.DBNull:
            WriteNull();
            return;
#endif
        }
      }
#if !PocketPC && !NET20
      else if (value is DateTimeOffset)
      {
        WriteValue((DateTimeOffset)value);
        return;
      }
#endif
      else if (value is byte[])
      {
        WriteValue((byte[])value);
        return;
      }
      else if (value is Guid)
      {
        WriteValue((Guid)value);
        return;
      }
      else if (value is Uri)
      {
        WriteValue((Uri)value);
        return;
      }
      else if (value is TimeSpan)
      {
        WriteValue((TimeSpan)value);
        return;
      }

      throw new ArgumentException("Unsupported type: {0}. Use the JsonSerializer class to get the object's JSON representation.".FormatWith(CultureInfo.InvariantCulture, value.GetType()));
    }
    #endregion

    /// <summary>
    /// Writes out a comment <code>/*...*/</code> containing the specified text. 
    /// </summary>
    /// <param name="text">Text to place inside the comment.</param>
    public virtual void WriteComment(string text)
    {
      AutoComplete(JsonToken.Comment);
    }

    /// <summary>
    /// Writes out the given white space.
    /// </summary>
    /// <param name="ws">The string of white space characters.</param>
    public virtual void WriteWhitespace(string ws)
    {
      if (ws != null)
      {
        if (!StringUtils.IsWhiteSpace(ws))
          throw new JsonWriterException("Only white space characters should be used.");
      }
    }


    void IDisposable.Dispose()
    {
      Dispose(true);
    }

    private void Dispose(bool disposing)
    {
      if (_currentState != State.Closed)
        Close();
    }
  }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer TrackerRealm
Canada Canada
Charles Wiebe and John Hansen are Microsoft .NET software architect/designer, for Windows Forms and ASP.NET solutions.
Charles specializes in the application layer – Web Parts and other ways of building GUI’s. John specializes in high capacity object oriented systems and connectivity.

John and Charles are co-developers of Jetfire – a .net open source, multi-user, application domain specific language (DSL) with syntax heavily based on C# and Java. The goal of Jetfire is to allow power users to quickly and easily develop and deploy applications, in much the same way as Excel allows powers users to quickly develop spread sheets.

Their latest project is Jazz - a compact, modular framework that allows new, or existing, applications to easily employ roles, states, ACLs and void safety. Jazz allows complete workflows to be implemented in only a few hundred lines of C# or VB code.

Comments and Discussions