Selaa lähdekoodia

Merge pull request #942 from jskeet/json-exception

Created a new exception for JSON failures.
Jan Tattermusch 10 vuotta sitten
vanhempi
commit
25c045a180

+ 1 - 0
Makefile.am

@@ -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                                   \

+ 22 - 22
csharp/src/Google.Protobuf.Test/JsonParserTest.cs

@@ -370,19 +370,19 @@ namespace Google.Protobuf
         }
         }
 
 
         [Test]
         [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 + "}";
             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")]
-        [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 + "}";
             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));
         }
         }
     }
     }
 }
 }

+ 2 - 2
csharp/src/Google.Protobuf.Test/JsonTokenizerTest.cs

@@ -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());
         }
         }
     }
     }
 }
 }

+ 1 - 0
csharp/src/Google.Protobuf/Google.Protobuf.csproj

@@ -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" />

+ 53 - 0
csharp/src/Google.Protobuf/InvalidJsonException.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)
+        {
+        }
+    }
+}

+ 4 - 0
csharp/src/Google.Protobuf/JsonParser.cs

@@ -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();

+ 26 - 16
csharp/src/Google.Protobuf/JsonTokenizer.cs

@@ -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);
+            }
         }
         }
     }
     }
 }
 }

+ 2 - 0
csharp/src/Google.Protobuf/MessageParser.cs

@@ -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();