parent
2b86884659
commit
afe844bc95
9 changed files with 1178 additions and 2 deletions
@ -0,0 +1,26 @@ |
||||
using System.IO; |
||||
using System.Text; |
||||
using Google.ProtocolBuffers.Serialization; |
||||
using NUnit.Framework; |
||||
|
||||
namespace Google.ProtocolBuffers.CompatTests |
||||
{ |
||||
[TestFixture] |
||||
public class JsonCompatibilityTests : CompatibilityTests |
||||
{ |
||||
protected override object SerializeMessage<TMessage, TBuilder>(TMessage message) |
||||
{ |
||||
StringWriter sw = new StringWriter(); |
||||
new JsonFormatWriter(sw) |
||||
.Formatted() |
||||
.WriteMessage(message); |
||||
return sw.ToString(); |
||||
} |
||||
|
||||
protected override TBuilder DeerializeMessage<TMessage, TBuilder>(object message, TBuilder builder, ExtensionRegistry registry) |
||||
{ |
||||
new JsonFormatReader((string)message).Merge(builder); |
||||
return builder; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,342 @@ |
||||
using System; |
||||
using System.IO; |
||||
using Google.ProtocolBuffers.Serialization; |
||||
using NUnit.Framework; |
||||
using Google.ProtocolBuffers.TestProtos; |
||||
|
||||
namespace Google.ProtocolBuffers |
||||
{ |
||||
[TestFixture] |
||||
public class TestWriterFormatJson |
||||
{ |
||||
protected string Content; |
||||
[System.Diagnostics.DebuggerNonUserCode] |
||||
protected void FormatterAssert<TMessage>(TMessage message, params string[] expecting) where TMessage : IMessageLite |
||||
{ |
||||
StringWriter sw = new StringWriter(); |
||||
new JsonFormatWriter(sw).WriteMessage(message); |
||||
|
||||
Content = sw.ToString(); |
||||
|
||||
ExtensionRegistry registry = ExtensionRegistry.CreateInstance(); |
||||
UnitTestXmlSerializerTestProtoFile.RegisterAllExtensions(registry); |
||||
|
||||
IMessageLite copy = |
||||
new JsonFormatReader(Content) |
||||
.Merge(message.WeakCreateBuilderForType(), registry).WeakBuild(); |
||||
|
||||
Assert.AreEqual(typeof(TMessage), copy.GetType()); |
||||
Assert.AreEqual(message, copy); |
||||
foreach (string expect in expecting) |
||||
Assert.IsTrue(Content.IndexOf(expect) >= 0, "Expected to find content '{0}' in: \r\n{1}", expect, Content); |
||||
} |
||||
|
||||
[Test] |
||||
public void TestJsonFormatted() |
||||
{ |
||||
TestXmlMessage message = TestXmlMessage.CreateBuilder() |
||||
.SetValid(true) |
||||
.SetNumber(0x1010) |
||||
.AddChildren(TestXmlMessage.Types.Children.CreateBuilder()) |
||||
.AddChildren(TestXmlMessage.Types.Children.CreateBuilder().AddOptions(EnumOptions.ONE)) |
||||
.AddChildren(TestXmlMessage.Types.Children.CreateBuilder().AddOptions(EnumOptions.ONE).AddOptions(EnumOptions.TWO)) |
||||
.AddChildren(TestXmlMessage.Types.Children.CreateBuilder().SetBinary(ByteString.CopyFromUtf8("abc"))) |
||||
.Build(); |
||||
|
||||
StringWriter sw = new StringWriter(); |
||||
new JsonFormatWriter(sw).Formatted() |
||||
.WriteMessage(message); |
||||
|
||||
string json = sw.ToString(); |
||||
|
||||
TestXmlMessage copy = new JsonFormatReader(json) |
||||
.Merge(TestXmlMessage.CreateBuilder()).Build(); |
||||
Assert.AreEqual(message, copy); |
||||
} |
||||
|
||||
[Test] |
||||
public void TestEmptyMessage() |
||||
{ |
||||
FormatterAssert( |
||||
TestXmlChild.CreateBuilder() |
||||
.Build(), |
||||
@"{}" |
||||
); |
||||
} |
||||
[Test] |
||||
public void TestRepeatedField() |
||||
{ |
||||
FormatterAssert( |
||||
TestXmlChild.CreateBuilder() |
||||
.AddOptions(EnumOptions.ONE) |
||||
.AddOptions(EnumOptions.TWO) |
||||
.Build(), |
||||
@"{""options"":[""ONE"",""TWO""]}" |
||||
); |
||||
} |
||||
[Test] |
||||
public void TestNestedEmptyMessage() |
||||
{ |
||||
FormatterAssert( |
||||
TestXmlMessage.CreateBuilder() |
||||
.SetChild(TestXmlChild.CreateBuilder().Build()) |
||||
.Build(), |
||||
@"{""child"":{}}" |
||||
); |
||||
} |
||||
[Test] |
||||
public void TestNestedMessage() |
||||
{ |
||||
FormatterAssert( |
||||
TestXmlMessage.CreateBuilder() |
||||
.SetChild(TestXmlChild.CreateBuilder().AddOptions(EnumOptions.TWO).Build()) |
||||
.Build(), |
||||
@"{""child"":{""options"":[""TWO""]}}" |
||||
); |
||||
} |
||||
[Test] |
||||
public void TestBooleanTypes() |
||||
{ |
||||
FormatterAssert( |
||||
TestXmlMessage.CreateBuilder() |
||||
.SetValid(true) |
||||
.Build(), |
||||
@"{""valid"":true}" |
||||
); |
||||
} |
||||
[Test] |
||||
public void TestFullMessage() |
||||
{ |
||||
FormatterAssert( |
||||
TestXmlMessage.CreateBuilder() |
||||
.SetValid(true) |
||||
.SetText("text") |
||||
.AddTextlines("a") |
||||
.AddTextlines("b") |
||||
.AddTextlines("c") |
||||
.SetNumber(0x1010101010) |
||||
.AddNumbers(1) |
||||
.AddNumbers(2) |
||||
.AddNumbers(3) |
||||
.SetChild(TestXmlChild.CreateBuilder().AddOptions(EnumOptions.ONE).SetBinary(ByteString.CopyFrom(new byte[1]))) |
||||
.AddChildren(TestXmlMessage.Types.Children.CreateBuilder().AddOptions(EnumOptions.TWO).SetBinary(ByteString.CopyFrom(new byte[2]))) |
||||
.AddChildren(TestXmlMessage.Types.Children.CreateBuilder().AddOptions(EnumOptions.THREE).SetBinary(ByteString.CopyFrom(new byte[3]))) |
||||
.Build(), |
||||
@"""text"":""text""", |
||||
@"[""a"",""b"",""c""]", |
||||
@"[1,2,3]", |
||||
@"""child"":{", |
||||
@"""children"":[{", |
||||
@"AA==", |
||||
@"AAA=", |
||||
@"AAAA", |
||||
0x1010101010L.ToString() |
||||
); |
||||
} |
||||
[Test] |
||||
public void TestMessageWithXmlText() |
||||
{ |
||||
FormatterAssert( |
||||
TestXmlMessage.CreateBuilder() |
||||
.SetText("<text></text>") |
||||
.Build(), |
||||
@"{""text"":""<text><\/text>""}" |
||||
); |
||||
} |
||||
[Test] |
||||
public void TestWithEscapeChars() |
||||
{ |
||||
FormatterAssert( |
||||
TestXmlMessage.CreateBuilder() |
||||
.SetText(" \t <- \"leading space and trailing\" -> \\ \xef54 \x0000 \xFF \xFFFF \b \f \r \n \t ") |
||||
.Build(), |
||||
"{\"text\":\" \\t <- \\\"leading space and trailing\\\" -> \\\\ \\uef54 \\u0000 \\u00ff \\uffff \\b \\f \\r \\n \\t \"}" |
||||
); |
||||
} |
||||
[Test] |
||||
public void TestWithExtensionText() |
||||
{ |
||||
FormatterAssert( |
||||
TestXmlMessage.CreateBuilder() |
||||
.SetValid(false) |
||||
.SetExtension(UnitTestXmlSerializerTestProtoFile.ExtensionText, " extension text value ! ") |
||||
.Build(), |
||||
@"{""valid"":false,""extension_text"":"" extension text value ! ""}" |
||||
); |
||||
} |
||||
[Test] |
||||
public void TestWithExtensionNumber() |
||||
{ |
||||
FormatterAssert( |
||||
TestXmlMessage.CreateBuilder() |
||||
.SetExtension(UnitTestXmlSerializerTestProtoFile.ExtensionMessage, |
||||
new TestXmlExtension.Builder().SetNumber(42).Build()) |
||||
.Build(), |
||||
@"{""number"":42}" |
||||
); |
||||
} |
||||
[Test] |
||||
public void TestWithExtensionArray() |
||||
{ |
||||
FormatterAssert( |
||||
TestXmlMessage.CreateBuilder() |
||||
.AddExtension(UnitTestXmlSerializerTestProtoFile.ExtensionNumber, 100) |
||||
.AddExtension(UnitTestXmlSerializerTestProtoFile.ExtensionNumber, 101) |
||||
.AddExtension(UnitTestXmlSerializerTestProtoFile.ExtensionNumber, 102) |
||||
.Build(), |
||||
@"{""extension_number"":[100,101,102]}" |
||||
); |
||||
} |
||||
[Test] |
||||
public void TestWithExtensionEnum() |
||||
{ |
||||
FormatterAssert( |
||||
TestXmlMessage.CreateBuilder() |
||||
.SetExtension(UnitTestXmlSerializerTestProtoFile.ExtensionEnum, EnumOptions.ONE) |
||||
.Build(), |
||||
@"{""extension_enum"":""ONE""}" |
||||
); |
||||
} |
||||
[Test] |
||||
public void TestMessageWithExtensions() |
||||
{ |
||||
FormatterAssert( |
||||
TestXmlMessage.CreateBuilder() |
||||
.SetValid(true) |
||||
.SetText("text") |
||||
.SetExtension(UnitTestXmlSerializerTestProtoFile.ExtensionText, "extension text") |
||||
.SetExtension(UnitTestXmlSerializerTestProtoFile.ExtensionMessage, new TestXmlExtension.Builder().SetNumber(42).Build()) |
||||
.AddExtension(UnitTestXmlSerializerTestProtoFile.ExtensionNumber, 100) |
||||
.AddExtension(UnitTestXmlSerializerTestProtoFile.ExtensionNumber, 101) |
||||
.AddExtension(UnitTestXmlSerializerTestProtoFile.ExtensionNumber, 102) |
||||
.SetExtension(UnitTestXmlSerializerTestProtoFile.ExtensionEnum, EnumOptions.ONE) |
||||
.Build(), |
||||
@"""text"":""text""", |
||||
@"""valid"":true", |
||||
@"""extension_enum"":""ONE""", |
||||
@"""extension_text"":""extension text""", |
||||
@"""extension_number"":[100,101,102]", |
||||
@"""extension_message"":{""number"":42}" |
||||
); |
||||
} |
||||
[Test] |
||||
public void TestMessageMissingExtensions() |
||||
{ |
||||
TestXmlMessage original = TestXmlMessage.CreateBuilder() |
||||
.SetValid(true) |
||||
.SetText("text") |
||||
.SetExtension(UnitTestXmlSerializerTestProtoFile.ExtensionText, " extension text value ! ") |
||||
.SetExtension(UnitTestXmlSerializerTestProtoFile.ExtensionMessage, new TestXmlExtension.Builder().SetNumber(42).Build()) |
||||
.AddExtension(UnitTestXmlSerializerTestProtoFile.ExtensionNumber, 100) |
||||
.AddExtension(UnitTestXmlSerializerTestProtoFile.ExtensionNumber, 101) |
||||
.AddExtension(UnitTestXmlSerializerTestProtoFile.ExtensionNumber, 102) |
||||
.SetExtension(UnitTestXmlSerializerTestProtoFile.ExtensionEnum, EnumOptions.ONE) |
||||
.Build(); |
||||
|
||||
TestXmlMessage message = original.ToBuilder() |
||||
.ClearExtension(UnitTestXmlSerializerTestProtoFile.ExtensionText) |
||||
.ClearExtension(UnitTestXmlSerializerTestProtoFile.ExtensionMessage) |
||||
.ClearExtension(UnitTestXmlSerializerTestProtoFile.ExtensionNumber) |
||||
.ClearExtension(UnitTestXmlSerializerTestProtoFile.ExtensionEnum) |
||||
.Build(); |
||||
|
||||
JsonFormatWriter writer = new JsonFormatWriter(); |
||||
writer.WriteMessage(original); |
||||
Content = writer.ToString(); |
||||
|
||||
IMessageLite copy = new JsonFormatReader(Content) |
||||
.Merge(message.CreateBuilderForType()).Build(); |
||||
|
||||
Assert.AreNotEqual(original, message); |
||||
Assert.AreNotEqual(original, copy); |
||||
Assert.AreEqual(message, copy); |
||||
} |
||||
[Test] |
||||
public void TestMergeFields() |
||||
{ |
||||
TestXmlMessage.Builder builder = TestXmlMessage.CreateBuilder(); |
||||
builder.MergeFrom(new JsonFormatReader("\"valid\": true")); |
||||
builder.MergeFrom(new JsonFormatReader("\"text\": \"text\", \"number\": \"411\"")); |
||||
Assert.AreEqual(true, builder.Valid); |
||||
Assert.AreEqual("text", builder.Text); |
||||
Assert.AreEqual(411, builder.Number); |
||||
} |
||||
[Test] |
||||
public void TestMessageArray() |
||||
{ |
||||
JsonFormatWriter writer = new JsonFormatWriter().Formatted(); |
||||
using (writer.StartArray()) |
||||
{ |
||||
writer.WriteMessage(TestXmlMessage.CreateBuilder().SetNumber(1).AddTextlines("a").Build()); |
||||
writer.WriteMessage(TestXmlMessage.CreateBuilder().SetNumber(2).AddTextlines("b").Build()); |
||||
writer.WriteMessage(TestXmlMessage.CreateBuilder().SetNumber(3).AddTextlines("c").Build()); |
||||
} |
||||
string json = writer.ToString(); |
||||
JsonFormatReader reader = new JsonFormatReader(json); |
||||
|
||||
TestXmlMessage.Builder builder = TestXmlMessage.CreateBuilder(); |
||||
int ordinal = 0; |
||||
|
||||
foreach (JsonFormatReader r in reader.EnumerateArray()) |
||||
{ |
||||
r.Merge(builder); |
||||
Assert.AreEqual(++ordinal, builder.Number); |
||||
} |
||||
Assert.AreEqual(3, ordinal); |
||||
Assert.AreEqual(3, builder.TextlinesCount); |
||||
} |
||||
[Test] |
||||
public void TestNestedMessageArray() |
||||
{ |
||||
JsonFormatWriter writer = new JsonFormatWriter(); |
||||
using (writer.StartArray()) |
||||
{ |
||||
using (writer.StartArray()) |
||||
{ |
||||
writer.WriteMessage(TestXmlMessage.CreateBuilder().SetNumber(1).AddTextlines("a").Build()); |
||||
writer.WriteMessage(TestXmlMessage.CreateBuilder().SetNumber(2).AddTextlines("b").Build()); |
||||
} |
||||
using (writer.StartArray()) |
||||
writer.WriteMessage(TestXmlMessage.CreateBuilder().SetNumber(3).AddTextlines("c").Build()); |
||||
} |
||||
string json = writer.ToString(); |
||||
JsonFormatReader reader = new JsonFormatReader(json); |
||||
|
||||
TestXmlMessage.Builder builder = TestXmlMessage.CreateBuilder(); |
||||
int ordinal = 0; |
||||
|
||||
foreach (JsonFormatReader r in reader.EnumerateArray()) |
||||
foreach (JsonFormatReader r2 in r.EnumerateArray()) |
||||
{ |
||||
r2.Merge(builder); |
||||
Assert.AreEqual(++ordinal, builder.Number); |
||||
} |
||||
Assert.AreEqual(3, ordinal); |
||||
Assert.AreEqual(3, builder.TextlinesCount); |
||||
} |
||||
[Test, ExpectedException(typeof(FormatException))] |
||||
public void FailWithEmptyText() |
||||
{ |
||||
new JsonFormatReader("") |
||||
.Merge(TestXmlMessage.CreateBuilder()); |
||||
} |
||||
[Test, ExpectedException(typeof(FormatException))] |
||||
public void FailWithUnexpectedValue() |
||||
{ |
||||
new JsonFormatReader("{{}}") |
||||
.Merge(TestXmlMessage.CreateBuilder()); |
||||
} |
||||
[Test, ExpectedException(typeof(FormatException))] |
||||
public void FailWithUnQuotedName() |
||||
{ |
||||
new JsonFormatReader("{name:{}}") |
||||
.Merge(TestXmlMessage.CreateBuilder()); |
||||
} |
||||
[Test, ExpectedException(typeof(FormatException))] |
||||
public void FailWithUnexpectedType() |
||||
{ |
||||
new JsonFormatReader("{\"valid\":{}}") |
||||
.Merge(TestXmlMessage.CreateBuilder()); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,270 @@ |
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Globalization; |
||||
using System.IO; |
||||
using System.Text; |
||||
|
||||
namespace Google.ProtocolBuffers.Serialization |
||||
{ |
||||
/// <summary> |
||||
/// JSon Tokenizer used by JsonFormatReader |
||||
/// </summary> |
||||
class JsonTextCursor |
||||
{ |
||||
public enum JsType { String, Number, Object, Array, True, False, Null } |
||||
|
||||
private readonly char[] _buffer; |
||||
private int _bufferPos; |
||||
private readonly TextReader _input; |
||||
private int _lineNo, _linePos; |
||||
|
||||
public JsonTextCursor(char[] input) |
||||
{ |
||||
_input = null; |
||||
_buffer = input; |
||||
_bufferPos = 0; |
||||
_next = Peek(); |
||||
_lineNo = 1; |
||||
} |
||||
|
||||
public JsonTextCursor(TextReader input) |
||||
{ |
||||
_input = input; |
||||
_next = Peek(); |
||||
_lineNo = 1; |
||||
} |
||||
|
||||
private int Peek() |
||||
{ |
||||
if (_input != null) |
||||
return _input.Peek(); |
||||
else if (_bufferPos < _buffer.Length) |
||||
return _buffer[_bufferPos]; |
||||
else |
||||
return -1; |
||||
} |
||||
|
||||
private int Read() |
||||
{ |
||||
if (_input != null) |
||||
return _input.Read(); |
||||
else if (_bufferPos < _buffer.Length) |
||||
return _buffer[_bufferPos++]; |
||||
else |
||||
return -1; |
||||
} |
||||
|
||||
int _next; |
||||
public Char NextChar { get { SkipWhitespace(); return (char)_next; } } |
||||
|
||||
#region Assert(...) |
||||
[System.Diagnostics.DebuggerNonUserCode] |
||||
private string CharDisplay(int ch) |
||||
{ |
||||
return ch == -1 ? "EOF" : |
||||
(ch > 32 && ch < 127) ? String.Format("'{0}'", (char)ch) : |
||||
String.Format("'\\u{0:x4}'", ch); |
||||
} |
||||
[System.Diagnostics.DebuggerNonUserCode] |
||||
private void Assert(bool cond, char expected) |
||||
{ |
||||
if (!cond) |
||||
{ |
||||
throw new FormatException( |
||||
String.Format(CultureInfo.InvariantCulture, |
||||
"({0}:{1}) error: Unexpected token {2}, expected: {3}.", |
||||
_lineNo, _linePos, |
||||
CharDisplay(_next), |
||||
CharDisplay(expected) |
||||
)); |
||||
} |
||||
} |
||||
[System.Diagnostics.DebuggerNonUserCode] |
||||
public void Assert(bool cond, string message) |
||||
{ |
||||
if (!cond) |
||||
{ |
||||
throw new FormatException( |
||||
String.Format(CultureInfo.InvariantCulture, |
||||
"({0},{1}) error: {2}", _lineNo, _linePos, message)); |
||||
} |
||||
} |
||||
[System.Diagnostics.DebuggerNonUserCode] |
||||
public void Assert(bool cond, string format, params object[] args) |
||||
{ |
||||
if (!cond) |
||||
{ |
||||
if (args != null && args.Length > 0) |
||||
format = String.Format(format, args); |
||||
throw new FormatException( |
||||
String.Format(CultureInfo.InvariantCulture, |
||||
"({0},{1}) error: {2}", _lineNo, _linePos, format)); |
||||
} |
||||
} |
||||
#endregion |
||||
|
||||
private char ReadChar() |
||||
{ |
||||
int ch = Read(); |
||||
Assert(ch != -1, "Unexpected end of file."); |
||||
if (ch == '\n') |
||||
{ |
||||
_lineNo++; |
||||
_linePos = 0; |
||||
} |
||||
else if (ch != '\r') |
||||
{ |
||||
_linePos++; |
||||
} |
||||
_next = Peek(); |
||||
return (char)ch; |
||||
} |
||||
|
||||
public void Consume(char ch) { Assert(TryConsume(ch), ch); } |
||||
public bool TryConsume(char ch) |
||||
{ |
||||
SkipWhitespace(); |
||||
if (_next == ch) |
||||
{ |
||||
ReadChar(); |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
public void Consume(string sequence) |
||||
{ |
||||
SkipWhitespace(); |
||||
|
||||
foreach (char ch in sequence) |
||||
Assert(ch == ReadChar(), "Expected token '{0}'.", sequence); |
||||
} |
||||
|
||||
public void SkipWhitespace() |
||||
{ |
||||
int chnext = _next; |
||||
while (chnext != -1) |
||||
{ |
||||
if (!Char.IsWhiteSpace((char)chnext)) |
||||
break; |
||||
ReadChar(); |
||||
chnext = _next; |
||||
} |
||||
} |
||||
|
||||
public string ReadString() |
||||
{ |
||||
SkipWhitespace(); |
||||
Consume('"'); |
||||
StringBuilder sb = new StringBuilder(); |
||||
while (_next != '"') |
||||
{ |
||||
if (_next == '\\') |
||||
{ |
||||
Consume('\\');//skip the escape |
||||
char ch = ReadChar(); |
||||
switch (ch) |
||||
{ |
||||
case 'b': sb.Append('\b'); break; |
||||
case 'f': sb.Append('\f'); break; |
||||
case 'n': sb.Append('\n'); break; |
||||
case 'r': sb.Append('\r'); break; |
||||
case 't': sb.Append('\t'); break; |
||||
case 'u': |
||||
{ |
||||
string hex = new string(new char[] { ReadChar(), ReadChar(), ReadChar(), ReadChar() }); |
||||
int result; |
||||
Assert(int.TryParse(hex, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out result), |
||||
"Expected a 4-character hex specifier."); |
||||
sb.Append((char)result); |
||||
break; |
||||
} |
||||
default: |
||||
sb.Append(ch); break; |
||||
} |
||||
} |
||||
else |
||||
{ |
||||
Assert(_next != '\n' && _next != '\r' && _next != '\f' && _next != -1, '"'); |
||||
sb.Append(ReadChar()); |
||||
} |
||||
} |
||||
Consume('"'); |
||||
return sb.ToString(); |
||||
} |
||||
|
||||
public string ReadNumber() |
||||
{ |
||||
SkipWhitespace(); |
||||
|
||||
StringBuilder sb = new StringBuilder(); |
||||
if (_next == '-') |
||||
sb.Append(ReadChar()); |
||||
Assert(_next >= '0' && _next <= '9', "Expected a numeric type."); |
||||
while ((_next >= '0' && _next <= '9') || _next == '.') |
||||
sb.Append(ReadChar()); |
||||
if (_next == 'e' || _next == 'E') |
||||
{ |
||||
sb.Append(ReadChar()); |
||||
if (_next == '-' || _next == '+') |
||||
sb.Append(ReadChar()); |
||||
Assert(_next >= '0' && _next <= '9', "Expected a numeric type."); |
||||
while (_next >= '0' && _next <= '9') |
||||
sb.Append(ReadChar()); |
||||
} |
||||
return sb.ToString(); |
||||
} |
||||
|
||||
public JsType ReadVariant(out object value) |
||||
{ |
||||
SkipWhitespace(); |
||||
switch (_next) |
||||
{ |
||||
case 'n': Consume("null"); value = null; return JsType.Null; |
||||
case 't': Consume("true"); value = true; return JsType.True; |
||||
case 'f': Consume("false"); value = false; return JsType.False; |
||||
case '"': value = ReadString(); return JsType.String; |
||||
case '{': |
||||
{ |
||||
Consume('{'); |
||||
while (NextChar != '}') |
||||
{ |
||||
ReadString(); |
||||
Consume(':'); |
||||
object tmp; |
||||
ReadVariant(out tmp); |
||||
if (!TryConsume(',')) |
||||
break; |
||||
} |
||||
Consume('}'); |
||||
value = null; |
||||
return JsType.Object; |
||||
} |
||||
case '[': |
||||
{ |
||||
Consume('['); |
||||
List<object> values = new List<object>(); |
||||
while (NextChar != ']') |
||||
{ |
||||
object tmp; |
||||
ReadVariant(out tmp); |
||||
values.Add(tmp); |
||||
if (!TryConsume(',')) |
||||
break; |
||||
} |
||||
Consume(']'); |
||||
value = values.ToArray(); |
||||
return JsType.Array; |
||||
} |
||||
default: |
||||
if ((_next >= '0' && _next <= '9') || _next == '-') |
||||
{ |
||||
value = ReadNumber(); |
||||
return JsType.Number; |
||||
} |
||||
Assert(false, "Expected a value."); |
||||
throw new FormatException(); |
||||
} |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue