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/IDeepCloneable.cs \
csharp/src/Google.Protobuf/IMessage.cs \
csharp/src/Google.Protobuf/InvalidJsonException.cs \
csharp/src/Google.Protobuf/InvalidProtocolBufferException.cs \
csharp/src/Google.Protobuf/JsonFormatter.cs \
csharp/src/Google.Protobuf/JsonParser.cs \

@ -370,19 +370,19 @@ namespace Google.Protobuf
}
[Test]
[TestCase("+0")]
[TestCase("00")]
[TestCase("-00")]
[TestCase("--1")]
[TestCase("+1")]
[TestCase("1.5", Ignore = true, Reason = "Desired behaviour unclear")]
[TestCase("1e10")]
[TestCase("2147483648")]
[TestCase("-2147483649")]
public void NumberToInt32_Invalid(string jsonValue)
[TestCase("+0", typeof(InvalidJsonException))]
[TestCase("00", typeof(InvalidJsonException))]
[TestCase("-00", typeof(InvalidJsonException))]
[TestCase("--1", typeof(InvalidJsonException))]
[TestCase("+1", typeof(InvalidJsonException))]
[TestCase("1.5", typeof(InvalidProtocolBufferException), Ignore = true, Reason = "Desired behaviour unclear")]
[TestCase("1e10", typeof(InvalidProtocolBufferException))]
[TestCase("2147483648", typeof(InvalidProtocolBufferException))]
[TestCase("-2147483649", typeof(InvalidProtocolBufferException))]
public void NumberToInt32_Invalid(string jsonValue, System.Type expectedExceptionType)
{
string json = "{ \"singleInt32\": " + jsonValue + "}";
Assert.Throws<InvalidProtocolBufferException>(() => TestAllTypes.Parser.ParseJson(json));
Assert.Throws(expectedExceptionType, () => TestAllTypes.Parser.ParseJson(json));
}
[Test]
@ -486,7 +486,7 @@ namespace Google.Protobuf
public void NumberToDouble_Invalid(string jsonValue)
{
string json = "{ \"singleDouble\": " + jsonValue + "}";
Assert.Throws<InvalidProtocolBufferException>(() => TestAllTypes.Parser.ParseJson(json));
Assert.Throws<InvalidJsonException>(() => TestAllTypes.Parser.ParseJson(json));
}
[Test]
@ -506,17 +506,17 @@ namespace Google.Protobuf
}
[Test]
[TestCase("3.402824e38")]
[TestCase("-3.402824e38")]
[TestCase("1,0")]
[TestCase("1.0.0")]
[TestCase("+1")]
[TestCase("00")]
[TestCase("--1")]
public void NumberToFloat_Invalid(string jsonValue)
[TestCase("3.402824e38", typeof(InvalidProtocolBufferException))]
[TestCase("-3.402824e38", typeof(InvalidProtocolBufferException))]
[TestCase("1,0", typeof(InvalidJsonException))]
[TestCase("1.0.0", typeof(InvalidJsonException))]
[TestCase("+1", typeof(InvalidJsonException))]
[TestCase("00", typeof(InvalidJsonException))]
[TestCase("--1", typeof(InvalidJsonException))]
public void NumberToFloat_Invalid(string jsonValue, System.Type expectedExceptionType)
{
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,
@ -721,7 +721,7 @@ namespace Google.Protobuf
public void DataAfterObject()
{
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.Throws<InvalidProtocolBufferException>(() => tokenizer.Next());
Assert.Throws<InvalidJsonException>(() => tokenizer.Next());
}
[Test]
@ -346,7 +346,7 @@ namespace Google.Protobuf
}
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="FrameworkPortability.cs" />
<Compile Include="IDeepCloneable.cs" />
<Compile Include="InvalidJsonException.cs" />
<Compile Include="JsonFormatter.cs" />
<Compile Include="JsonParser.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>
/// <typeparam name="T">The type of message to create.</typeparam>
/// <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()
{
return Parse<T>(new StringReader(json));
@ -347,6 +349,8 @@ namespace Google.Protobuf
/// </summary>
/// <typeparam name="T">The type of message to create.</typeparam>
/// <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()
{
T message = new T();

@ -88,6 +88,7 @@ namespace Google.Protobuf
/// </remarks>
/// <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="InvalidJsonException">The input text does not comply with RFC 7159</exception>
internal JsonToken Next()
{
if (bufferedToken != null)
@ -182,7 +183,7 @@ namespace Google.Protobuf
ValidateAndModifyStateForValue("Invalid state to read a number token: ");
return JsonToken.Value(number);
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)
{
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");
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 (haveHighSurrogate)
{
throw new InvalidProtocolBufferException("Invalid use of surrogate pair code units");
throw reader.CreateException("Invalid use of surrogate pair code units");
}
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.
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);
value.Append(c);
@ -260,7 +261,7 @@ namespace Google.Protobuf
case 'u':
return ReadUnicodeEscape();
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
{
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;
}
@ -306,11 +307,11 @@ namespace Google.Protobuf
char? next = reader.Read();
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])
{
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)
{
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");
if (first < '0' || first > '9')
{
throw new InvalidProtocolBufferException("Invalid numeric literal");
throw reader.CreateException("Invalid numeric literal");
}
builder.Append(first);
int digitCount;
char? next = ConsumeDigits(builder, out digitCount);
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;
}
@ -382,7 +383,7 @@ namespace Google.Protobuf
char? next = ConsumeDigits(builder, out digitCount);
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;
}
@ -393,7 +394,7 @@ namespace Google.Protobuf
char? next = reader.Read();
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 == '+')
{
@ -407,7 +408,7 @@ namespace Google.Protobuf
next = ConsumeDigits(builder, out digitCount);
if (digitCount == 0)
{
throw new InvalidProtocolBufferException("Invalid numeric literal: exponent without value");
throw reader.CreateException("Invalid numeric literal: exponent without value");
}
return next;
}
@ -615,7 +616,7 @@ namespace Google.Protobuf
char? next = Read();
if (next == null)
{
throw new InvalidProtocolBufferException(messageOnFailure);
throw CreateException(messageOnFailure);
}
return next.Value;
}
@ -628,6 +629,15 @@ namespace Google.Protobuf
}
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>
/// <param name="json">The JSON to parse.</param>
/// <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)
{
T message = factory();

Loading…
Cancel
Save