This is an automated email from the ASF dual-hosted git repository.

zoltan pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/avro.git


The following commit(s) were added to refs/heads/main by this push:
     new 4f6132641 AVRO-2032: [C#] Add support for NaN, Infinity and -Infinity 
in JsonDecoder (#3070)
4f6132641 is described below

commit 4f61326413f1a35ccfc4e3b93b5c304bb06c3d0a
Author: Zoltan Csizmadia <[email protected]>
AuthorDate: Fri Nov 22 11:27:56 2024 -0600

    AVRO-2032: [C#] Add support for NaN, Infinity and -Infinity in JsonDecoder 
(#3070)
    
    * Add support for IEEE 754 strings in json decoder
    
    * Remove redundent casting
    
    ---------
    
    Co-authored-by: Zoltan Csizmadia <[email protected]>
---
 lang/csharp/src/apache/main/IO/JsonDecoder.cs    | 55 ++++++++++++++++++++++--
 lang/csharp/src/apache/test/IO/JsonCodecTests.cs | 54 +++++++++++++++++++++++
 2 files changed, 105 insertions(+), 4 deletions(-)

diff --git a/lang/csharp/src/apache/main/IO/JsonDecoder.cs 
b/lang/csharp/src/apache/main/IO/JsonDecoder.cs
index 0e45ebc50..549ead26d 100644
--- a/lang/csharp/src/apache/main/IO/JsonDecoder.cs
+++ b/lang/csharp/src/apache/main/IO/JsonDecoder.cs
@@ -190,10 +190,25 @@ namespace Avro.IO
                 reader.Read();
                 return result;
             }
-            else
+            else if (reader.TokenType == JsonToken.String)
             {
-                throw TypeError("float");
+                string str = Convert.ToString(reader.Value);
+                reader.Read();
+                if (IsNaNString(str))
+                {
+                    return float.NaN;
+                }
+                else if (IsPositiveInfinityString(str))
+                {
+                    return float.PositiveInfinity;
+                }
+                else if (IsNegativeInfinityString(str))
+                {
+                    return float.NegativeInfinity;
+                }
             }
+
+            throw TypeError("float");
         }
 
         /// <inheritdoc />
@@ -206,10 +221,25 @@ namespace Avro.IO
                 reader.Read();
                 return result;
             }
-            else
+            else if (reader.TokenType == JsonToken.String)
             {
-                throw TypeError("double");
+                string str = Convert.ToString(reader.Value);
+                reader.Read();
+                if (IsNaNString(str))
+                {
+                    return double.NaN;
+                }
+                else if (IsPositiveInfinityString(str))
+                {
+                    return double.PositiveInfinity;
+                }
+                else if (IsNegativeInfinityString(str))
+                {
+                    return double.NegativeInfinity;
+                }
             }
+
+            throw TypeError("double");
         }
 
         /// <inheritdoc />
@@ -766,6 +796,23 @@ namespace Avro.IO
             }
         }
 
+        private bool IsNaNString(string str)
+        {
+            return str.Equals("NaN", StringComparison.Ordinal);
+        }
+
+        private bool IsPositiveInfinityString(string str)
+        {
+            return str.Equals("Infinity", StringComparison.Ordinal) ||
+                   str.Equals("INF", StringComparison.Ordinal);
+        }
+
+        private bool IsNegativeInfinityString(string str)
+        {
+            return str.Equals("-Infinity", StringComparison.Ordinal) ||
+                   str.Equals("-INF", StringComparison.Ordinal);
+        }
+
         private AvroTypeException TypeError(string type)
         {
             return new AvroTypeException("Expected " + type + ". Got " + 
reader.TokenType);
diff --git a/lang/csharp/src/apache/test/IO/JsonCodecTests.cs 
b/lang/csharp/src/apache/test/IO/JsonCodecTests.cs
index 5b05c93e5..fe2183a2f 100644
--- a/lang/csharp/src/apache/test/IO/JsonCodecTests.cs
+++ b/lang/csharp/src/apache/test/IO/JsonCodecTests.cs
@@ -268,6 +268,60 @@ namespace Avro.Test
             }
         }
 
+        [Test]
+        [TestCase("float", "0", (float)0)]
+        [TestCase("float", "1", (float)1)]
+        [TestCase("float", "1.0", (float)1.0)]
+        [TestCase("double", "0", (double)0)]
+        [TestCase("double", "1", (double)1)]
+        [TestCase("double", "1.0", 1.0)]
+        [TestCase("float", "\"NaN\"", float.NaN)]
+        [TestCase("float", "\"Infinity\"", float.PositiveInfinity)]
+        [TestCase("float", "\"INF\"", float.PositiveInfinity)]
+        [TestCase("float", "\"-Infinity\"", float.NegativeInfinity)]
+        [TestCase("float", "\"-INF\"", float.NegativeInfinity)]
+        [TestCase("double", "\"NaN\"", double.NaN)]
+        [TestCase("double", "\"Infinity\"", double.PositiveInfinity)]
+        [TestCase("double", "\"INF\"", double.PositiveInfinity)]
+        [TestCase("double", "\"-Infinity\"", double.NegativeInfinity)]
+        [TestCase("double", "\"-INF\"", double.NegativeInfinity)]
+        [TestCase("float", "\"\"", null)]
+        [TestCase("float", "\"unknown\"", null)]
+        [TestCase("float", "\"nan\"", null)]
+        [TestCase("float", "\"infinity\"", null)]
+        [TestCase("float", "\"inf\"", null)]
+        [TestCase("float", "\"-infinity\"", null)]
+        [TestCase("float", "\"-inf\"", null)]
+        [TestCase("double", "\"\"", null)]
+        [TestCase("double", "\"unknown\"", null)]
+        [TestCase("double", "\"nan\"", null)]
+        [TestCase("double", "\"infinity\"", null)]
+        [TestCase("double", "\"inf\"", null)]
+        [TestCase("double", "\"-infinity\"", null)]
+        [TestCase("double", "\"-inf\"", null)]
+        [TestCase("double", "\"-inf\"", null)]
+        public void TestJsonDecodeFloatDouble(string typeStr, string valueStr, 
object expected)
+        {
+            string def = 
$"{{\"type\":\"record\",\"name\":\"X\",\"fields\":[{{\"type\":\"{typeStr}\",\"name\":\"Value\"}}]}}";
+            Schema schema = Schema.Parse(def);
+            DatumReader<GenericRecord> reader = new 
GenericDatumReader<GenericRecord>(schema, schema);
+
+            string record = $"{{\"Value\":{valueStr}}}";
+            Decoder decoder = new JsonDecoder(schema, record);
+            try
+            {
+                GenericRecord r = reader.Read(null, decoder);
+                Assert.AreEqual(expected, r["Value"]);
+            }
+            catch (AvroTypeException)
+            {
+                if (expected != null)
+                {
+                    throw;
+                }
+            }
+        }
+
         [TestCase("{ \"s\": \"1900-01-01T00:00:00Z\" }", 
"1900-01-01T00:00:00Z")]
         [TestCase("{ \"s\": \"1900-01-01T00:00:00.0000000Z\" }", 
"1900-01-01T00:00:00.0000000Z")]
         [TestCase("{ \"s\": \"1900-01-01T00:00:00\" }", "1900-01-01T00:00:00")]

Reply via email to