123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541 |
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.IO;
- using System.Text;
- using Google.ProtocolBuffers.Descriptors;
- namespace Google.ProtocolBuffers.Serialization
- {
- /// <summary>
- /// JsonFormatWriter is a .NET 2.0 friendly json formatter for proto buffer messages. For .NET 3.5
- /// you may also use the XmlFormatWriter with an XmlWriter created by the
- /// <see cref="System.Runtime.Serialization.Json.JsonReaderWriterFactory">JsonReaderWriterFactory</see>.
- /// </summary>
- public abstract class JsonFormatWriter : AbstractTextWriter
- {
- #region buffering implementations
- private class JsonTextWriter : JsonFormatWriter
- {
- private readonly char[] _buffer;
- private TextWriter _output;
- private int _bufferPos;
- public JsonTextWriter(TextWriter output)
- {
- _buffer = new char[4096];
- _bufferPos = 0;
- _output = output;
- _counter.Add(0);
- }
- /// <summary>
- /// Returns the output of TextWriter.ToString() where TextWriter is the ctor argument.
- /// </summary>
- public override string ToString()
- {
- Flush();
- if (_output != null)
- {
- return _output.ToString();
- }
- return new String(_buffer, 0, _bufferPos);
- }
- protected override void WriteToOutput(char[] chars, int offset, int len)
- {
- if (_bufferPos + len >= _buffer.Length)
- {
- if (_output == null)
- {
- _output = new StringWriter(new StringBuilder(_buffer.Length*2 + len));
- }
- Flush();
- }
- if (len < _buffer.Length)
- {
- if (len <= 12)
- {
- int stop = offset + len;
- for (int i = offset; i < stop; i++)
- {
- _buffer[_bufferPos++] = chars[i];
- }
- }
- else
- {
- Buffer.BlockCopy(chars, offset << 1, _buffer, _bufferPos << 1, len << 1);
- _bufferPos += len;
- }
- }
- else
- {
- _output.Write(chars, offset, len);
- }
- }
- protected override void WriteToOutput(char ch)
- {
- if (_bufferPos >= _buffer.Length)
- {
- Flush();
- }
- _buffer[_bufferPos++] = ch;
- }
- public override void Flush()
- {
- if (_bufferPos > 0 && _output != null)
- {
- _output.Write(_buffer, 0, _bufferPos);
- _bufferPos = 0;
- }
- base.Flush();
- }
- }
- private class JsonStreamWriter : JsonFormatWriter
- {
- #if SILVERLIGHT || COMPACT_FRAMEWORK
- static readonly Encoding Encoding = new UTF8Encoding(false);
- #else
- private static readonly Encoding Encoding = Encoding.ASCII;
- #endif
- private readonly byte[] _buffer;
- private Stream _output;
- private int _bufferPos;
- public JsonStreamWriter(Stream output)
- {
- _buffer = new byte[8192];
- _bufferPos = 0;
- _output = output;
- _counter.Add(0);
- }
- protected override void WriteToOutput(char[] chars, int offset, int len)
- {
- if (_bufferPos + len >= _buffer.Length)
- {
- Flush();
- }
- if (len < _buffer.Length)
- {
- if (len <= 12)
- {
- int stop = offset + len;
- for (int i = offset; i < stop; i++)
- {
- _buffer[_bufferPos++] = (byte) chars[i];
- }
- }
- else
- {
- _bufferPos += Encoding.GetBytes(chars, offset, len, _buffer, _bufferPos);
- }
- }
- else
- {
- byte[] temp = Encoding.GetBytes(chars, offset, len);
- _output.Write(temp, 0, temp.Length);
- }
- }
- protected override void WriteToOutput(char ch)
- {
- if (_bufferPos >= _buffer.Length)
- {
- Flush();
- }
- _buffer[_bufferPos++] = (byte) ch;
- }
- public override void Flush()
- {
- if (_bufferPos > 0 && _output != null)
- {
- _output.Write(_buffer, 0, _bufferPos);
- _bufferPos = 0;
- }
- base.Flush();
- }
- }
- #endregion
- //Tracks the writer depth and the array element count at that depth.
- private readonly List<int> _counter;
- //True if the top-level of the writer is an array as opposed to a single message.
- private bool _isArray;
- /// <summary>
- /// Constructs a JsonFormatWriter, use the ToString() member to extract the final Json on completion.
- /// </summary>
- protected JsonFormatWriter()
- {
- _counter = new List<int>();
- }
- /// <summary>
- /// Constructs a JsonFormatWriter, use ToString() to extract the final output
- /// </summary>
- public static JsonFormatWriter CreateInstance()
- {
- return new JsonTextWriter(null);
- }
- /// <summary>
- /// Constructs a JsonFormatWriter to output to the given text writer
- /// </summary>
- public static JsonFormatWriter CreateInstance(TextWriter output)
- {
- return new JsonTextWriter(output);
- }
- /// <summary>
- /// Constructs a JsonFormatWriter to output to the given stream
- /// </summary>
- public static JsonFormatWriter CreateInstance(Stream output)
- {
- return new JsonStreamWriter(output);
- }
- /// <summary> Write to the output stream </summary>
- protected void WriteToOutput(string format, params object[] args)
- {
- WriteToOutput(String.Format(format, args));
- }
- /// <summary> Write to the output stream </summary>
- protected void WriteToOutput(string text)
- {
- WriteToOutput(text.ToCharArray(), 0, text.Length);
- }
- /// <summary> Write to the output stream </summary>
- protected abstract void WriteToOutput(char ch);
- /// <summary> Write to the output stream </summary>
- protected abstract void WriteToOutput(char[] chars, int offset, int len);
- /// <summary> Sets the output formatting to use Environment.NewLine with 4-character indentions </summary>
- public JsonFormatWriter Formatted()
- {
- NewLine = FrameworkPortability.NewLine;
- Indent = " ";
- Whitespace = " ";
- return this;
- }
- /// <summary> Gets or sets the characters to use for the new-line, default = empty </summary>
- public string NewLine { get; set; }
- /// <summary> Gets or sets the text to use for indenting, default = empty </summary>
- public string Indent { get; set; }
- /// <summary> Gets or sets the whitespace to use to separate the text, default = empty </summary>
- public string Whitespace { get; set; }
- private void Seperator()
- {
- if (_counter.Count == 0)
- {
- throw new InvalidOperationException("Mismatched open/close in Json writer.");
- }
- int index = _counter.Count - 1;
- if (_counter[index] > 0)
- {
- WriteToOutput(',');
- }
- WriteLine(String.Empty);
- _counter[index] = _counter[index] + 1;
- }
- private void WriteLine(string content)
- {
- if (!String.IsNullOrEmpty(NewLine))
- {
- WriteToOutput(NewLine);
- for (int i = 1; i < _counter.Count; i++)
- {
- WriteToOutput(Indent);
- }
- }
- else if (!String.IsNullOrEmpty(Whitespace))
- {
- WriteToOutput(Whitespace);
- }
- WriteToOutput(content);
- }
- private void WriteName(string field)
- {
- Seperator();
- if (!String.IsNullOrEmpty(field))
- {
- WriteToOutput('"');
- WriteToOutput(field);
- WriteToOutput('"');
- WriteToOutput(':');
- if (!String.IsNullOrEmpty(Whitespace))
- {
- WriteToOutput(Whitespace);
- }
- }
- }
- private void EncodeText(string value)
- {
- char[] text = value.ToCharArray();
- int len = text.Length;
- int pos = 0;
- while (pos < len)
- {
- int next = pos;
- while (next < len && text[next] >= 32 && text[next] < 127 && text[next] != '\\' && text[next] != '/' &&
- text[next] != '"')
- {
- next++;
- }
- WriteToOutput(text, pos, next - pos);
- if (next < len)
- {
- switch (text[next])
- {
- case '"':
- WriteToOutput(@"\""");
- break;
- case '\\':
- WriteToOutput(@"\\");
- break;
- //odd at best to escape '/', most Json implementations don't, but it is defined in the rfc-4627
- case '/':
- WriteToOutput(@"\/");
- break;
- case '\b':
- WriteToOutput(@"\b");
- break;
- case '\f':
- WriteToOutput(@"\f");
- break;
- case '\n':
- WriteToOutput(@"\n");
- break;
- case '\r':
- WriteToOutput(@"\r");
- break;
- case '\t':
- WriteToOutput(@"\t");
- break;
- default:
- WriteToOutput(@"\u{0:x4}", (int) text[next]);
- break;
- }
- next++;
- }
- pos = next;
- }
- }
- /// <summary>
- /// Writes a String value
- /// </summary>
- protected override void WriteAsText(string field, string textValue, object typedValue)
- {
- WriteName(field);
- if (typedValue is bool || typedValue is int || typedValue is uint || typedValue is long ||
- typedValue is ulong || typedValue is double || typedValue is float)
- {
- WriteToOutput(textValue);
- }
- else
- {
- WriteToOutput('"');
- if (typedValue is string)
- {
- EncodeText(textValue);
- }
- else
- {
- WriteToOutput(textValue);
- }
- WriteToOutput('"');
- }
- }
- /// <summary>
- /// Writes a Double value
- /// </summary>
- protected override void Write(string field, double value)
- {
- if (double.IsNaN(value) || double.IsNegativeInfinity(value) || double.IsPositiveInfinity(value))
- {
- throw new InvalidOperationException("This format does not support NaN, Infinity, or -Infinity");
- }
- base.Write(field, value);
- }
- /// <summary>
- /// Writes a Single value
- /// </summary>
- protected override void Write(string field, float value)
- {
- if (float.IsNaN(value) || float.IsNegativeInfinity(value) || float.IsPositiveInfinity(value))
- {
- throw new InvalidOperationException("This format does not support NaN, Infinity, or -Infinity");
- }
- base.Write(field, value);
- }
- // Treat enum as string
- protected override void WriteEnum(string field, int number, string name)
- {
- Write(field, name);
- }
- /// <summary>
- /// Writes an array of field values
- /// </summary>
- protected override void WriteArray(FieldType type, string field, IEnumerable items)
- {
- IEnumerator enumerator = items.GetEnumerator();
- try
- {
- if (!enumerator.MoveNext())
- {
- return;
- }
- }
- finally
- {
- if (enumerator is IDisposable)
- {
- ((IDisposable) enumerator).Dispose();
- }
- }
- WriteName(field);
- WriteToOutput("[");
- _counter.Add(0);
- base.WriteArray(type, String.Empty, items);
- _counter.RemoveAt(_counter.Count - 1);
- WriteLine("]");
- }
- /// <summary>
- /// Writes a message
- /// </summary>
- protected override void WriteMessageOrGroup(string field, IMessageLite message)
- {
- WriteName(field);
- WriteMessage(message);
- }
- /// <summary>
- /// Writes the message to the the formatted stream.
- /// </summary>
- public override void WriteMessage(IMessageLite message)
- {
- WriteMessageStart();
- message.WriteTo(this);
- WriteMessageEnd();
- }
- /// <summary>
- /// Used to write the root-message preamble, in json this is the left-curly brace '{'.
- /// After this call you can call IMessageLite.MergeTo(...) and complete the message with
- /// a call to WriteMessageEnd().
- /// </summary>
- public override void WriteMessageStart()
- {
- if (_isArray)
- {
- Seperator();
- }
- WriteToOutput("{");
- _counter.Add(0);
- }
- /// <summary>
- /// Used to complete a root-message previously started with a call to WriteMessageStart()
- /// </summary>
- public override void WriteMessageEnd()
- {
- _counter.RemoveAt(_counter.Count - 1);
- WriteLine("}");
- Flush();
- }
- /// <summary>
- /// Used in streaming arrays of objects to the writer
- /// </summary>
- /// <example>
- /// <code>
- /// using(writer.StartArray())
- /// foreach(IMessageLite m in messages)
- /// writer.WriteMessage(m);
- /// </code>
- /// </example>
- public sealed class JsonArray : IDisposable
- {
- private JsonFormatWriter _writer;
- internal JsonArray(JsonFormatWriter writer)
- {
- _writer = writer;
- _writer.WriteToOutput("[");
- _writer._counter.Add(0);
- }
- /// <summary>
- /// Causes the end of the array character to be written.
- /// </summary>
- private void EndArray()
- {
- if (_writer != null)
- {
- _writer._counter.RemoveAt(_writer._counter.Count - 1);
- _writer.WriteLine("]");
- _writer.Flush();
- }
- _writer = null;
- }
- void IDisposable.Dispose()
- {
- EndArray();
- }
- }
- /// <summary>
- /// Used to write an array of messages as the output rather than a single message.
- /// </summary>
- /// <example>
- /// <code>
- /// using(writer.StartArray())
- /// foreach(IMessageLite m in messages)
- /// writer.WriteMessage(m);
- /// </code>
- /// </example>
- public JsonArray StartArray()
- {
- if (_isArray)
- {
- Seperator();
- }
- _isArray = true;
- return new JsonArray(this);
- }
- }
- }
|