Merge pull request #942 from jskeet/json-exception

Created a new exception for JSON failures.
pull/895/merge
Jan Tattermusch 9 years ago
commit 25c045a180
  1. 1
      Makefile.am
  2. 44
      csharp/src/Google.Protobuf.Test/JsonParserTest.cs
  3. 4
      csharp/src/Google.Protobuf.Test/JsonTokenizerTest.cs
  4. 1
      csharp/src/Google.Protobuf/Google.Protobuf.csproj
  5. 53
      csharp/src/Google.Protobuf/InvalidJsonException.cs
  6. 4
      csharp/src/Google.Protobuf/JsonParser.cs
  7. 42
      csharp/src/Google.Protobuf/JsonTokenizer.cs
  8. 2
      csharp/src/Google.Protobuf/MessageParser.cs

@ -119,6 +119,7 @@ csharp_EXTRA_DIST= \
csharp/src/Google.Protobuf/Google.Protobuf.nuspec \ csharp/src/Google.Protobuf/Google.Protobuf.nuspec \
csharp/src/Google.Protobuf/IDeepCloneable.cs \ csharp/src/Google.Protobuf/IDeepCloneable.cs \
csharp/src/Google.Protobuf/IMessage.cs \ csharp/src/Google.Protobuf/IMessage.cs \
csharp/src/Google.Protobuf/InvalidJsonException.cs \
csharp/src/Google.Protobuf/InvalidProtocolBufferException.cs \ csharp/src/Google.Protobuf/InvalidProtocolBufferException.cs \
csharp/src/Google.Protobuf/JsonFormatter.cs \ csharp/src/Google.Protobuf/JsonFormatter.cs \
csharp/src/Google.Protobuf/JsonParser.cs \ csharp/src/Google.Protobuf/JsonParser.cs \

@ -370,19 +370,19 @@ namespace Google.Protobuf
} }
[Test] [Test]
[TestCase("+0")] [TestCase("+0", typeof(InvalidJsonException))]
[TestCase("00")] [TestCase("00", typeof(InvalidJsonException))]
[TestCase("-00")] [TestCase("-00", typeof(InvalidJsonException))]
[TestCase("--1")] [TestCase("--1", typeof(InvalidJsonException))]
[TestCase("+1")] [TestCase("+1", typeof(InvalidJsonException))]
[TestCase("1.5", Ignore = true, Reason = "Desired behaviour unclear")] [TestCase("1.5", typeof(InvalidProtocolBufferException), Ignore = true, Reason = "Desired behaviour unclear")]
[TestCase("1e10")] [TestCase("1e10", typeof(InvalidProtocolBufferException))]
[TestCase("2147483648")] [TestCase("2147483648", typeof(InvalidProtocolBufferException))]
[TestCase("-2147483649")] [TestCase("-2147483649", typeof(InvalidProtocolBufferException))]
public void NumberToInt32_Invalid(string jsonValue) public void NumberToInt32_Invalid(string jsonValue, System.Type expectedExceptionType)
{ {
string json = "{ \"singleInt32\": " + jsonValue + "}"; string json = "{ \"singleInt32\": " + jsonValue + "}";
Assert.Throws<InvalidProtocolBufferException>(() => TestAllTypes.Parser.ParseJson(json)); Assert.Throws(expectedExceptionType, () => TestAllTypes.Parser.ParseJson(json));
} }
[Test] [Test]
@ -486,7 +486,7 @@ namespace Google.Protobuf
public void NumberToDouble_Invalid(string jsonValue) public void NumberToDouble_Invalid(string jsonValue)
{ {
string json = "{ \"singleDouble\": " + jsonValue + "}"; string json = "{ \"singleDouble\": " + jsonValue + "}";
Assert.Throws<InvalidProtocolBufferException>(() => TestAllTypes.Parser.ParseJson(json)); Assert.Throws<InvalidJsonException>(() => TestAllTypes.Parser.ParseJson(json));
} }
[Test] [Test]
@ -506,17 +506,17 @@ namespace Google.Protobuf
} }
[Test] [Test]
[TestCase("3.402824e38")] [TestCase("3.402824e38", typeof(InvalidProtocolBufferException))]
[TestCase("-3.402824e38")] [TestCase("-3.402824e38", typeof(InvalidProtocolBufferException))]
[TestCase("1,0")] [TestCase("1,0", typeof(InvalidJsonException))]
[TestCase("1.0.0")] [TestCase("1.0.0", typeof(InvalidJsonException))]
[TestCase("+1")] [TestCase("+1", typeof(InvalidJsonException))]
[TestCase("00")] [TestCase("00", typeof(InvalidJsonException))]
[TestCase("--1")] [TestCase("--1", typeof(InvalidJsonException))]
public void NumberToFloat_Invalid(string jsonValue) public void NumberToFloat_Invalid(string jsonValue, System.Type expectedExceptionType)
{ {
string json = "{ \"singleFloat\": " + jsonValue + "}"; string json = "{ \"singleFloat\": " + jsonValue + "}";
Assert.Throws<InvalidProtocolBufferException>(() => TestAllTypes.Parser.ParseJson(json)); Assert.Throws(expectedExceptionType, () => TestAllTypes.Parser.ParseJson(json));
} }
// The simplest way of testing that the value has parsed correctly is to reformat it, // The simplest way of testing that the value has parsed correctly is to reformat it,
@ -721,7 +721,7 @@ namespace Google.Protobuf
public void DataAfterObject() public void DataAfterObject()
{ {
string json = "{} 10"; string json = "{} 10";
Assert.Throws<InvalidProtocolBufferException>(() => TestAllTypes.Parser.ParseJson(json)); Assert.Throws<InvalidJsonException>(() => TestAllTypes.Parser.ParseJson(json));
} }
} }
} }

@ -223,7 +223,7 @@ namespace Google.Protobuf
{ {
Assert.IsNotNull(tokenizer.Next()); Assert.IsNotNull(tokenizer.Next());
} }
Assert.Throws<InvalidProtocolBufferException>(() => tokenizer.Next()); Assert.Throws<InvalidJsonException>(() => tokenizer.Next());
} }
[Test] [Test]
@ -346,7 +346,7 @@ namespace Google.Protobuf
} }
Assert.AreEqual(expectedTokens[i], actualToken); Assert.AreEqual(expectedTokens[i], actualToken);
} }
Assert.Throws<InvalidProtocolBufferException>(() => tokenizer.Next()); Assert.Throws<InvalidJsonException>(() => tokenizer.Next());
} }
} }
} }

@ -84,6 +84,7 @@
<Compile Include="FieldCodec.cs" /> <Compile Include="FieldCodec.cs" />
<Compile Include="FrameworkPortability.cs" /> <Compile Include="FrameworkPortability.cs" />
<Compile Include="IDeepCloneable.cs" /> <Compile Include="IDeepCloneable.cs" />
<Compile Include="InvalidJsonException.cs" />
<Compile Include="JsonFormatter.cs" /> <Compile Include="JsonFormatter.cs" />
<Compile Include="JsonParser.cs" /> <Compile Include="JsonParser.cs" />
<Compile Include="JsonToken.cs" /> <Compile Include="JsonToken.cs" />

@ -0,0 +1,53 @@
#region Copyright notice and license
// Protocol Buffers - Google's data interchange format
// Copyright 2015 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using System.IO;
namespace Google.Protobuf
{
/// <summary>
/// Thrown when an attempt is made to parse invalid JSON, e.g. using
/// a non-string property key, or including a redundant comma. Parsing a protocol buffer
/// message represented in JSON using <see cref="JsonParser"/> can throw both this
/// exception and <see cref="InvalidProtocolBufferException"/> depending on the situation. This
/// exception is only thrown for "pure JSON" errors, whereas <c>InvalidProtocolBufferException</c>
/// is thrown when the JSON may be valid in and of itself, but cannot be parsed as a protocol buffer
/// message.
/// </summary>
public sealed class InvalidJsonException : IOException
{
internal InvalidJsonException(string message)
: base(message)
{
}
}
}

@ -337,6 +337,8 @@ namespace Google.Protobuf
/// </summary> /// </summary>
/// <typeparam name="T">The type of message to create.</typeparam> /// <typeparam name="T">The type of message to create.</typeparam>
/// <param name="json">The JSON to parse.</param> /// <param name="json">The JSON to parse.</param>
/// <exception cref="InvalidJsonException">The JSON does not comply with RFC 7159</exception>
/// <exception cref="InvalidProtocolBufferException">The JSON does not represent a Protocol Buffers message correctly</exception>
public T Parse<T>(string json) where T : IMessage, new() public T Parse<T>(string json) where T : IMessage, new()
{ {
return Parse<T>(new StringReader(json)); return Parse<T>(new StringReader(json));
@ -347,6 +349,8 @@ namespace Google.Protobuf
/// </summary> /// </summary>
/// <typeparam name="T">The type of message to create.</typeparam> /// <typeparam name="T">The type of message to create.</typeparam>
/// <param name="jsonReader">Reader providing the JSON to parse.</param> /// <param name="jsonReader">Reader providing the JSON to parse.</param>
/// <exception cref="InvalidJsonException">The JSON does not comply with RFC 7159</exception>
/// <exception cref="InvalidProtocolBufferException">The JSON does not represent a Protocol Buffers message correctly</exception>
public T Parse<T>(TextReader jsonReader) where T : IMessage, new() public T Parse<T>(TextReader jsonReader) where T : IMessage, new()
{ {
T message = new T(); T message = new T();

@ -88,6 +88,7 @@ namespace Google.Protobuf
/// </remarks> /// </remarks>
/// <returns>The next token in the stream. This is never null.</returns> /// <returns>The next token in the stream. This is never null.</returns>
/// <exception cref="InvalidOperationException">This method is called after an EndDocument token has been returned</exception> /// <exception cref="InvalidOperationException">This method is called after an EndDocument token has been returned</exception>
/// <exception cref="InvalidJsonException">The input text does not comply with RFC 7159</exception>
internal JsonToken Next() internal JsonToken Next()
{ {
if (bufferedToken != null) if (bufferedToken != null)
@ -182,7 +183,7 @@ namespace Google.Protobuf
ValidateAndModifyStateForValue("Invalid state to read a number token: "); ValidateAndModifyStateForValue("Invalid state to read a number token: ");
return JsonToken.Value(number); return JsonToken.Value(number);
default: default:
throw new InvalidProtocolBufferException("Invalid first character of token: " + next.Value); throw new InvalidJsonException("Invalid first character of token: " + next.Value);
} }
} }
} }
@ -191,7 +192,7 @@ namespace Google.Protobuf
{ {
if ((validStates & state) == 0) if ((validStates & state) == 0)
{ {
throw new InvalidProtocolBufferException(errorPrefix + state); throw reader.CreateException(errorPrefix + state);
} }
} }
@ -207,13 +208,13 @@ namespace Google.Protobuf
char c = reader.ReadOrFail("Unexpected end of text while reading string"); char c = reader.ReadOrFail("Unexpected end of text while reading string");
if (c < ' ') if (c < ' ')
{ {
throw new InvalidProtocolBufferException(string.Format(CultureInfo.InvariantCulture, "Invalid character in string literal: U+{0:x4}", (int) c)); throw reader.CreateException(string.Format(CultureInfo.InvariantCulture, "Invalid character in string literal: U+{0:x4}", (int) c));
} }
if (c == '"') if (c == '"')
{ {
if (haveHighSurrogate) if (haveHighSurrogate)
{ {
throw new InvalidProtocolBufferException("Invalid use of surrogate pair code units"); throw reader.CreateException("Invalid use of surrogate pair code units");
} }
return value.ToString(); return value.ToString();
} }
@ -226,7 +227,7 @@ namespace Google.Protobuf
// followed by an escaped low surrogate or vice versa... and that couldn't even be represented in UTF-8. // followed by an escaped low surrogate or vice versa... and that couldn't even be represented in UTF-8.
if (haveHighSurrogate != char.IsLowSurrogate(c)) if (haveHighSurrogate != char.IsLowSurrogate(c))
{ {
throw new InvalidProtocolBufferException("Invalid use of surrogate pair code units"); throw reader.CreateException("Invalid use of surrogate pair code units");
} }
haveHighSurrogate = char.IsHighSurrogate(c); haveHighSurrogate = char.IsHighSurrogate(c);
value.Append(c); value.Append(c);
@ -260,7 +261,7 @@ namespace Google.Protobuf
case 'u': case 'u':
return ReadUnicodeEscape(); return ReadUnicodeEscape();
default: default:
throw new InvalidProtocolBufferException(string.Format(CultureInfo.InvariantCulture, "Invalid character in character escape sequence: U+{0:x4}", (int) c)); throw reader.CreateException(string.Format(CultureInfo.InvariantCulture, "Invalid character in character escape sequence: U+{0:x4}", (int) c));
} }
} }
@ -288,7 +289,7 @@ namespace Google.Protobuf
} }
else else
{ {
throw new InvalidProtocolBufferException(string.Format(CultureInfo.InvariantCulture, "Invalid character in character escape sequence: U+{0:x4}", (int) c)); throw reader.CreateException(string.Format(CultureInfo.InvariantCulture, "Invalid character in character escape sequence: U+{0:x4}", (int) c));
} }
result = (result << 4) + nybble; result = (result << 4) + nybble;
} }
@ -306,11 +307,11 @@ namespace Google.Protobuf
char? next = reader.Read(); char? next = reader.Read();
if (next == null) if (next == null)
{ {
throw new InvalidProtocolBufferException("Unexpected end of text while reading literal token " + text); throw reader.CreateException("Unexpected end of text while reading literal token " + text);
} }
if (next.Value != text[i]) if (next.Value != text[i])
{ {
throw new InvalidProtocolBufferException("Unexpected character while reading literal token " + text); throw reader.CreateException("Unexpected character while reading literal token " + text);
} }
} }
} }
@ -354,7 +355,7 @@ namespace Google.Protobuf
} }
catch (OverflowException) catch (OverflowException)
{ {
throw new InvalidProtocolBufferException("Numeric value out of range: " + builder); throw reader.CreateException("Numeric value out of range: " + builder);
} }
} }
@ -363,14 +364,14 @@ namespace Google.Protobuf
char first = reader.ReadOrFail("Invalid numeric literal"); char first = reader.ReadOrFail("Invalid numeric literal");
if (first < '0' || first > '9') if (first < '0' || first > '9')
{ {
throw new InvalidProtocolBufferException("Invalid numeric literal"); throw reader.CreateException("Invalid numeric literal");
} }
builder.Append(first); builder.Append(first);
int digitCount; int digitCount;
char? next = ConsumeDigits(builder, out digitCount); char? next = ConsumeDigits(builder, out digitCount);
if (first == '0' && digitCount != 0) if (first == '0' && digitCount != 0)
{ {
throw new InvalidProtocolBufferException("Invalid numeric literal: leading 0 for non-zero value."); throw reader.CreateException("Invalid numeric literal: leading 0 for non-zero value.");
} }
return next; return next;
} }
@ -382,7 +383,7 @@ namespace Google.Protobuf
char? next = ConsumeDigits(builder, out digitCount); char? next = ConsumeDigits(builder, out digitCount);
if (digitCount == 0) if (digitCount == 0)
{ {
throw new InvalidProtocolBufferException("Invalid numeric literal: fraction with no trailing digits"); throw reader.CreateException("Invalid numeric literal: fraction with no trailing digits");
} }
return next; return next;
} }
@ -393,7 +394,7 @@ namespace Google.Protobuf
char? next = reader.Read(); char? next = reader.Read();
if (next == null) if (next == null)
{ {
throw new InvalidProtocolBufferException("Invalid numeric literal: exponent with no trailing digits"); throw reader.CreateException("Invalid numeric literal: exponent with no trailing digits");
} }
if (next == '-' || next == '+') if (next == '-' || next == '+')
{ {
@ -407,7 +408,7 @@ namespace Google.Protobuf
next = ConsumeDigits(builder, out digitCount); next = ConsumeDigits(builder, out digitCount);
if (digitCount == 0) if (digitCount == 0)
{ {
throw new InvalidProtocolBufferException("Invalid numeric literal: exponent without value"); throw reader.CreateException("Invalid numeric literal: exponent without value");
} }
return next; return next;
} }
@ -615,7 +616,7 @@ namespace Google.Protobuf
char? next = Read(); char? next = Read();
if (next == null) if (next == null)
{ {
throw new InvalidProtocolBufferException(messageOnFailure); throw CreateException(messageOnFailure);
} }
return next.Value; return next.Value;
} }
@ -628,6 +629,15 @@ namespace Google.Protobuf
} }
nextChar = c; nextChar = c;
} }
/// <summary>
/// Creates a new exception appropriate for the current state of the reader.
/// </summary>
internal InvalidJsonException CreateException(string message)
{
// TODO: Keep track of and use the location.
return new InvalidJsonException(message);
}
} }
} }
} }

@ -148,6 +148,8 @@ namespace Google.Protobuf
/// </summary> /// </summary>
/// <param name="json">The JSON to parse.</param> /// <param name="json">The JSON to parse.</param>
/// <returns>The parsed message.</returns> /// <returns>The parsed message.</returns>
/// <exception cref="InvalidJsonException">The JSON does not comply with RFC 7159</exception>
/// <exception cref="InvalidProtocolBufferException">The JSON does not represent a Protocol Buffers message correctly</exception>
public T ParseJson(string json) public T ParseJson(string json)
{ {
T message = factory(); T message = factory();

Loading…
Cancel
Save