Skip to content

Commit 4f61326

Browse files
zcsizmadiaZoltan Csizmadia
andauthored
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]>
1 parent b1ec3b9 commit 4f61326

File tree

2 files changed

+105
-4
lines changed

2 files changed

+105
-4
lines changed

lang/csharp/src/apache/main/IO/JsonDecoder.cs

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -190,10 +190,25 @@ public override float ReadFloat()
190190
reader.Read();
191191
return result;
192192
}
193-
else
193+
else if (reader.TokenType == JsonToken.String)
194194
{
195-
throw TypeError("float");
195+
string str = Convert.ToString(reader.Value);
196+
reader.Read();
197+
if (IsNaNString(str))
198+
{
199+
return float.NaN;
200+
}
201+
else if (IsPositiveInfinityString(str))
202+
{
203+
return float.PositiveInfinity;
204+
}
205+
else if (IsNegativeInfinityString(str))
206+
{
207+
return float.NegativeInfinity;
208+
}
196209
}
210+
211+
throw TypeError("float");
197212
}
198213

199214
/// <inheritdoc />
@@ -206,10 +221,25 @@ public override double ReadDouble()
206221
reader.Read();
207222
return result;
208223
}
209-
else
224+
else if (reader.TokenType == JsonToken.String)
210225
{
211-
throw TypeError("double");
226+
string str = Convert.ToString(reader.Value);
227+
reader.Read();
228+
if (IsNaNString(str))
229+
{
230+
return double.NaN;
231+
}
232+
else if (IsPositiveInfinityString(str))
233+
{
234+
return double.PositiveInfinity;
235+
}
236+
else if (IsNegativeInfinityString(str))
237+
{
238+
return double.NegativeInfinity;
239+
}
212240
}
241+
242+
throw TypeError("double");
213243
}
214244

215245
/// <inheritdoc />
@@ -766,6 +796,23 @@ public override bool Read()
766796
}
767797
}
768798

799+
private bool IsNaNString(string str)
800+
{
801+
return str.Equals("NaN", StringComparison.Ordinal);
802+
}
803+
804+
private bool IsPositiveInfinityString(string str)
805+
{
806+
return str.Equals("Infinity", StringComparison.Ordinal) ||
807+
str.Equals("INF", StringComparison.Ordinal);
808+
}
809+
810+
private bool IsNegativeInfinityString(string str)
811+
{
812+
return str.Equals("-Infinity", StringComparison.Ordinal) ||
813+
str.Equals("-INF", StringComparison.Ordinal);
814+
}
815+
769816
private AvroTypeException TypeError(string type)
770817
{
771818
return new AvroTypeException("Expected " + type + ". Got " + reader.TokenType);

lang/csharp/src/apache/test/IO/JsonCodecTests.cs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,60 @@ public void TestJsonDecoderNumeric(string type, object value)
268268
}
269269
}
270270

271+
[Test]
272+
[TestCase("float", "0", (float)0)]
273+
[TestCase("float", "1", (float)1)]
274+
[TestCase("float", "1.0", (float)1.0)]
275+
[TestCase("double", "0", (double)0)]
276+
[TestCase("double", "1", (double)1)]
277+
[TestCase("double", "1.0", 1.0)]
278+
[TestCase("float", "\"NaN\"", float.NaN)]
279+
[TestCase("float", "\"Infinity\"", float.PositiveInfinity)]
280+
[TestCase("float", "\"INF\"", float.PositiveInfinity)]
281+
[TestCase("float", "\"-Infinity\"", float.NegativeInfinity)]
282+
[TestCase("float", "\"-INF\"", float.NegativeInfinity)]
283+
[TestCase("double", "\"NaN\"", double.NaN)]
284+
[TestCase("double", "\"Infinity\"", double.PositiveInfinity)]
285+
[TestCase("double", "\"INF\"", double.PositiveInfinity)]
286+
[TestCase("double", "\"-Infinity\"", double.NegativeInfinity)]
287+
[TestCase("double", "\"-INF\"", double.NegativeInfinity)]
288+
[TestCase("float", "\"\"", null)]
289+
[TestCase("float", "\"unknown\"", null)]
290+
[TestCase("float", "\"nan\"", null)]
291+
[TestCase("float", "\"infinity\"", null)]
292+
[TestCase("float", "\"inf\"", null)]
293+
[TestCase("float", "\"-infinity\"", null)]
294+
[TestCase("float", "\"-inf\"", null)]
295+
[TestCase("double", "\"\"", null)]
296+
[TestCase("double", "\"unknown\"", null)]
297+
[TestCase("double", "\"nan\"", null)]
298+
[TestCase("double", "\"infinity\"", null)]
299+
[TestCase("double", "\"inf\"", null)]
300+
[TestCase("double", "\"-infinity\"", null)]
301+
[TestCase("double", "\"-inf\"", null)]
302+
[TestCase("double", "\"-inf\"", null)]
303+
public void TestJsonDecodeFloatDouble(string typeStr, string valueStr, object expected)
304+
{
305+
string def = $"{{\"type\":\"record\",\"name\":\"X\",\"fields\":[{{\"type\":\"{typeStr}\",\"name\":\"Value\"}}]}}";
306+
Schema schema = Schema.Parse(def);
307+
DatumReader<GenericRecord> reader = new GenericDatumReader<GenericRecord>(schema, schema);
308+
309+
string record = $"{{\"Value\":{valueStr}}}";
310+
Decoder decoder = new JsonDecoder(schema, record);
311+
try
312+
{
313+
GenericRecord r = reader.Read(null, decoder);
314+
Assert.AreEqual(expected, r["Value"]);
315+
}
316+
catch (AvroTypeException)
317+
{
318+
if (expected != null)
319+
{
320+
throw;
321+
}
322+
}
323+
}
324+
271325
[TestCase("{ \"s\": \"1900-01-01T00:00:00Z\" }", "1900-01-01T00:00:00Z")]
272326
[TestCase("{ \"s\": \"1900-01-01T00:00:00.0000000Z\" }", "1900-01-01T00:00:00.0000000Z")]
273327
[TestCase("{ \"s\": \"1900-01-01T00:00:00\" }", "1900-01-01T00:00:00")]

0 commit comments

Comments
 (0)