Skip to content

Commit 3cebe45

Browse files
Fix to_json handling of NaN and Infinity values
1 parent aefb3b6 commit 3cebe45

File tree

1 file changed

+54
-2
lines changed

1 file changed

+54
-2
lines changed

native/spark-expr/src/json_funcs/to_json.rs

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,12 +124,19 @@ fn array_to_json_string(arr: &Arc<dyn Array>, timezone: &str) -> Result<ArrayRef
124124
if let Some(struct_array) = arr.as_any().downcast_ref::<StructArray>() {
125125
struct_to_json(struct_array, timezone)
126126
} else {
127-
spark_cast(
127+
let array = spark_cast(
128128
ColumnarValue::Array(Arc::clone(arr)),
129129
&DataType::Utf8,
130130
&SparkCastOptions::new(EvalMode::Legacy, timezone, false),
131131
)?
132-
.into_array(arr.len())
132+
.into_array(arr.len())?;
133+
134+
let string_array = array
135+
.as_any()
136+
.downcast_ref::<StringArray>()
137+
.expect("Utf8 array");
138+
139+
Ok(normalize_special_floats(string_array))
133140
}
134141
}
135142

@@ -181,6 +188,23 @@ fn escape_string(input: &str) -> String {
181188
escaped_string
182189
}
183190

191+
fn normalize_special_floats(arr: &StringArray) -> ArrayRef {
192+
let mut builder = StringBuilder::with_capacity(arr.len(), arr.len() * 8);
193+
194+
for i in 0..arr.len() {
195+
if arr.is_null(i) {
196+
builder.append_null();
197+
} else {
198+
match arr.value(i) {
199+
"Infinity" | "-Infinity" | "NaN" => builder.append_null(),
200+
v => builder.append_value(v),
201+
}
202+
}
203+
}
204+
205+
Arc::new(builder.finish())
206+
}
207+
184208
fn struct_to_json(array: &StructArray, timezone: &str) -> Result<ArrayRef> {
185209
// get field names and escape any quotes
186210
let field_names: Vec<String> = array
@@ -331,6 +355,34 @@ mod test {
331355
Ok(())
332356
}
333357

358+
#[test]
359+
fn test_to_json_infinity() -> Result<()> {
360+
use arrow::array::{Float64Array, StructArray};
361+
use arrow::datatypes::{DataType, Field};
362+
363+
let values: ArrayRef = Arc::new(Float64Array::from(vec![
364+
Some(f64::INFINITY),
365+
Some(f64::NEG_INFINITY),
366+
Some(f64::NAN),
367+
Some(1.5),
368+
]));
369+
370+
let struct_array = StructArray::from(vec![(
371+
Arc::new(Field::new("a", DataType::Float64, true)),
372+
values,
373+
)]);
374+
375+
let json = struct_to_json(&struct_array, "UTC")?;
376+
let json = json.as_any().downcast_ref::<StringArray>().unwrap();
377+
378+
assert_eq!(r#"{}"#, json.value(0));
379+
assert_eq!(r#"{}"#, json.value(1));
380+
assert_eq!(r#"{}"#, json.value(2));
381+
assert_eq!(r#"{"a":1.5}"#, json.value(3));
382+
383+
Ok(())
384+
}
385+
334386
fn create_ints() -> Arc<PrimitiveArray<Int32Type>> {
335387
Arc::new(Int32Array::from(vec![
336388
Some(123),

0 commit comments

Comments
 (0)