blob: 70a1be76aea590257d3415f5cabbc87707a34344 [file]
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::{any::Any, borrow::Cow, fmt::Debug, sync::Arc};
use arrow::{
array::{
Array, ArrayRef, AsArray, StringArray, StringBuilder, StructArray, make_array,
new_null_array,
},
datatypes::{DataType, Field, Fields},
ffi::{FFI_ArrowArray, from_ffi_and_data_type, to_ffi},
};
use auron_jni_bridge::{conf, conf::BooleanConf, jni_call, jni_new_object, jni_new_string};
use datafusion::{
common::{Result, ScalarValue},
physical_plan::ColumnarValue,
};
use datafusion_ext_commons::{downcast_any, uda::UserDefinedArray};
use sonic_rs::{JsonContainerTrait, JsonValueTrait};
/// implement hive/spark's UDFGetJson
/// get_json_object(str, path) == get_parsed_json_object(parse_json(str), path)
pub fn spark_get_json_object(args: &[ColumnarValue]) -> Result<ColumnarValue> {
let json_string_array = match &args[0] {
ColumnarValue::Array(array) => array.clone(),
ColumnarValue::Scalar(scalar) => scalar.to_array_of_size(1)?,
};
let json_strings = json_string_array
.as_any()
.downcast_ref::<StringArray>()
.expect("Expected a StringArray");
let path_string = match &args[1] {
ColumnarValue::Scalar(ScalarValue::Utf8(str)) => match str {
Some(path) => path,
None => {
return Ok(ColumnarValue::Array(new_null_array(
&DataType::Utf8,
json_strings.len(),
)));
}
},
_ => unreachable!("path should be ScalarValue"),
};
let mut evaluator = match HiveGetJsonObjectEvaluator::try_new(path_string) {
Ok(evaluator) => evaluator,
Err(_) => {
return Ok(ColumnarValue::Array(new_null_array(
&DataType::Utf8,
json_strings.len(),
)));
}
};
let output = json_strings
.iter()
.map(|json_string| {
json_string.and_then(|s| match evaluator.evaluate(s) {
Ok(Some(matched)) => Some(matched),
_ => None,
})
})
.collect::<StringArray>();
Ok(ColumnarValue::Array(Arc::new(output)))
}
pub fn spark_parse_json(args: &[ColumnarValue]) -> Result<ColumnarValue> {
let json_string_array = match &args[0] {
ColumnarValue::Array(array) => array.clone(),
ColumnarValue::Scalar(scalar) => scalar.to_array_of_size(1)?,
};
let json_strings = json_string_array
.as_any()
.downcast_ref::<StringArray>()
.expect("Expected a StringArray");
let fallback_enabled = conf::PARSE_JSON_ERROR_FALLBACK.value().unwrap_or(false);
let json_values: Vec<Option<Arc<dyn Any + Send + Sync + 'static>>> = json_strings
.iter()
.map(|s| {
s.and_then(|s| {
// first try parsing with sonic-rs and fail-backing to serde-json
if s.is_empty() {
None
} else if let Ok(v) = sonic_rs::from_str::<sonic_rs::Value>(s) {
let v: Arc<dyn Any + Send + Sync> = Arc::new(ParsedJsonValue::Sonic(v));
Some(v)
} else if let Ok(v) = serde_json::from_str::<serde_json::Value>(s) {
let v: Arc<dyn Any + Send + Sync> = Arc::new(ParsedJsonValue::SerdeJson(v));
Some(v)
} else if fallback_enabled {
let v: Arc<dyn Any + Send + Sync> =
Arc::new(ParsedJsonValue::Fallback(s.to_owned()));
Some(v)
} else {
None
}
})
})
.collect();
Ok(ColumnarValue::Array(Arc::new(
UserDefinedArray::from_items(DataType::Binary, json_values),
)))
}
pub fn spark_get_parsed_json_object(args: &[ColumnarValue]) -> Result<ColumnarValue> {
let json_array = match &args[0] {
ColumnarValue::Array(array) => array
.as_any()
.downcast_ref::<UserDefinedArray>()
.expect("Expected a UserDefinedArray"),
ColumnarValue::Scalar(_) => unreachable!(),
};
let path_string = match &args[1] {
ColumnarValue::Scalar(ScalarValue::Utf8(str)) => match str {
Some(path) => path,
None => {
return Ok(ColumnarValue::Array(new_null_array(
&DataType::Utf8,
json_array.len(),
)));
}
},
_ => unreachable!("path should be ScalarValue"),
};
let mut evaluator = match HiveGetJsonObjectEvaluator::try_new(path_string) {
Ok(evaluator) => evaluator,
Err(_) => {
return Ok(ColumnarValue::Array(new_null_array(
&DataType::Utf8,
json_array.len(),
)));
}
};
let fallback_results = parse_fallback(path_string, json_array)?;
let mut fallback_results_iter = fallback_results.iter();
let output = StringArray::from_iter(json_array.iter().map(|value| {
value.as_ref().and_then(|value| -> Option<Cow<str>> {
let json_value = value
.downcast_ref::<ParsedJsonValue>()
.expect("Expected a ParsedJsonValue");
match json_value {
ParsedJsonValue::SerdeJson(v) => evaluator
.evaluate_with_value_serde_json(v)
.ok()
.unwrap_or(None)
.map(Cow::from),
ParsedJsonValue::Sonic(v) => evaluator
.evaluate_with_value_sonic(v)
.ok()
.unwrap_or(None)
.map(Cow::from),
ParsedJsonValue::Fallback(_) => fallback_results_iter
.next()
.expect("fallback result iterator must have a next element")
.map(Cow::from),
}
})
}));
Ok(ColumnarValue::Array(Arc::new(output)))
}
// this function is for json_tuple() usage, which can parse only top-level json
// fields
pub fn spark_get_parsed_json_simple_field(
parsed_json_array: &ArrayRef,
field: &String,
) -> Result<ArrayRef> {
let json_array = downcast_any!(parsed_json_array, UserDefinedArray)?;
let fallback_results = parse_fallback(field, json_array)?;
let mut fallback_results_iter = fallback_results.iter();
let output = StringArray::from_iter(json_array.iter().map(|value| {
value.as_ref().and_then(|value| {
let json_value = value
.downcast_ref::<ParsedJsonValue>()
.expect("Expected a ParsedJsonValue");
match json_value {
ParsedJsonValue::SerdeJson(v) => v
.as_object()
.and_then(|object| object.get(field))
.and_then(|v| serde_json_value_to_string(v).unwrap_or_default())
.map(Cow::from),
ParsedJsonValue::Sonic(v) => v
.as_object()
.and_then(|object| object.get(field))
.and_then(|v| sonic_value_to_string(v).unwrap_or_default())
.map(Cow::from),
ParsedJsonValue::Fallback(_) => fallback_results_iter
.next()
.expect("fallback result iterator must have a next element")
.map(Cow::from),
}
})
}));
Ok(Arc::new(output))
}
fn parse_fallback(json_path: &str, json_array: &UserDefinedArray) -> Result<StringArray> {
let fallback_results = if !conf::PARSE_JSON_ERROR_FALLBACK.value().unwrap_or(false) {
StringArray::new_null(0)
} else {
let mut fallback_jsons_array = StringBuilder::new();
for json in json_array.iter().flat_map(|value| {
value.as_ref().and_then(|value| -> Option<&str> {
let json_value = value
.downcast_ref::<ParsedJsonValue>()
.expect("Expected a ParsedJsonValue");
if let ParsedJsonValue::Fallback(json) = json_value {
return Some(json.as_ref());
}
None
})
}) {
fallback_jsons_array.append_value(json);
}
let fallback_jsons_array = fallback_jsons_array.finish();
if fallback_jsons_array.is_empty() {
StringArray::new_null(0)
} else {
let single_field = Fields::from(vec![Field::new("", DataType::Utf8, false)]);
let json_path = jni_new_string!(json_path)?;
let fallback_wrapper = jni_new_object!(AuronJsonFallbackWrapper(json_path.as_obj()))?;
let export_array = StructArray::new(
single_field.clone(),
vec![Arc::new(fallback_jsons_array)],
None,
);
let (mut ffi_export_array, _) = to_ffi(&export_array.to_data())?;
let mut ffi_import_array = FFI_ArrowArray::empty();
jni_call!(AuronJsonFallbackWrapper(fallback_wrapper.as_obj()).parseJsons(
&mut ffi_export_array as *mut FFI_ArrowArray as i64,
&mut ffi_import_array as *mut FFI_ArrowArray as i64,
) -> ())?;
let import_array = unsafe {
// safety: use arrow-ffi
from_ffi_and_data_type(ffi_import_array, DataType::Struct(single_field.clone()))?
};
let result_array = make_array(import_array.child_data()[0].clone());
result_array.as_string().clone()
}
};
Ok(fallback_results)
}
#[derive(Debug)]
enum ParsedJsonValue {
SerdeJson(serde_json::Value),
Sonic(sonic_rs::Value),
Fallback(String),
}
#[derive(Debug)]
enum HiveGetJsonObjectError {
InvalidJsonPath,
InvalidInput,
}
struct HiveGetJsonObjectEvaluator {
matchers: Vec<HiveGetJsonObjectMatcher>,
}
impl HiveGetJsonObjectEvaluator {
fn try_new(json_path: &str) -> std::result::Result<Self, HiveGetJsonObjectError> {
let mut evaluator = Self { matchers: vec![] };
let chars = json_path.chars();
let mut peekable = chars.peekable();
while let Some(matcher) = HiveGetJsonObjectMatcher::parse(&mut peekable)? {
evaluator.matchers.push(matcher);
}
if evaluator.matchers.first() != Some(&HiveGetJsonObjectMatcher::Root) {
return Err(HiveGetJsonObjectError::InvalidJsonPath);
}
evaluator.matchers.remove(0); // remove root matcher
if evaluator.matchers.contains(&HiveGetJsonObjectMatcher::Root) {
return Err(HiveGetJsonObjectError::InvalidJsonPath);
}
Ok(evaluator)
}
fn evaluate(
&mut self,
json_str: &str,
) -> std::result::Result<Option<String>, HiveGetJsonObjectError> {
// first try parsing with sonic-rs and fail-backing to serde-json
if let Ok(root_value) = sonic_rs::from_str::<sonic_rs::Value>(json_str)
&& let Ok(v) = self.evaluate_with_value_sonic(&root_value)
{
return Ok(v);
}
if let Ok(root_value) = serde_json::from_str::<serde_json::Value>(json_str)
&& let Ok(v) = self.evaluate_with_value_serde_json(&root_value)
{
return Ok(v);
}
Err(HiveGetJsonObjectError::InvalidInput)
}
fn evaluate_with_value_serde_json(
&mut self,
root_value: &serde_json::Value,
) -> std::result::Result<Option<String>, HiveGetJsonObjectError> {
let mut root_value: Cow<serde_json::Value> = Cow::Borrowed(root_value);
let mut value = root_value.as_ref();
for matcher in &self.matchers {
match matcher.evaluate_serde_json(value) {
Cow::Borrowed(evaluated) => {
value = evaluated;
}
Cow::Owned(evaluated) => {
root_value = Cow::Owned(evaluated);
value = root_value.as_ref();
}
}
}
serde_json_value_to_string(value)
}
fn evaluate_with_value_sonic(
&mut self,
root_value: &sonic_rs::Value,
) -> std::result::Result<Option<String>, HiveGetJsonObjectError> {
let mut root_value: Cow<sonic_rs::Value> = Cow::Borrowed(root_value);
let mut value = root_value.as_ref();
for matcher in &self.matchers {
match matcher.evaluate_sonic(value) {
Cow::Borrowed(evaluated) => {
value = evaluated;
}
Cow::Owned(evaluated) => {
root_value = Cow::Owned(evaluated);
value = root_value.as_ref();
}
}
}
sonic_value_to_string(value)
}
}
fn serde_json_value_to_string(
value: &serde_json::Value,
) -> std::result::Result<Option<String>, HiveGetJsonObjectError> {
match value {
serde_json::Value::Null => Ok(None),
serde_json::Value::String(string) => Ok(Some(string.to_string())),
serde_json::Value::Number(number) => Ok(Some(number.to_string())),
serde_json::Value::Bool(b) => Ok(Some(b.to_string())),
serde_json::Value::Array(_) | serde_json::Value::Object(_) => serde_json::to_string(value)
.map(Some)
.map_err(|_| HiveGetJsonObjectError::InvalidInput),
}
}
fn sonic_value_to_string(
value: &sonic_rs::Value,
) -> std::result::Result<Option<String>, HiveGetJsonObjectError> {
match value.get_type() {
sonic_rs::JsonType::Null => Ok(None),
sonic_rs::JsonType::String => Ok(value.as_str().map(|v| v.to_string())),
sonic_rs::JsonType::Number => Ok(value.as_number().map(|v| v.to_string())),
sonic_rs::JsonType::Boolean => Ok(value.as_bool().map(|v| v.to_string())),
_ => sonic_rs::to_string(value)
.map(Some)
.map_err(|_| HiveGetJsonObjectError::InvalidInput),
}
}
#[derive(Debug, PartialEq)]
enum HiveGetJsonObjectMatcher {
Root,
Child(String),
Subscript(usize),
SubscriptAll,
}
impl HiveGetJsonObjectMatcher {
fn parse(
chars: &mut std::iter::Peekable<std::str::Chars>,
) -> std::result::Result<Option<Self>, HiveGetJsonObjectError> {
match chars.peek() {
None => Ok(None),
Some('$') => {
chars.next();
Ok(Some(Self::Root))
}
Some('.') => {
chars.next();
loop {
match chars.peek() {
Some(' ') => {
chars.next();
}
Some(_) | None => {
break;
}
}
}
if chars.peek().cloned() == Some('[') {
return Self::parse(chars); // handle special case like
// $.aaa.[0].xxx
}
let mut child_name = String::new();
loop {
match chars.peek() {
Some('.') | Some('[') | None => {
break;
}
Some(c) => {
child_name.push(*c);
chars.next();
}
}
}
if child_name.is_empty() {
return Err(HiveGetJsonObjectError::InvalidJsonPath);
}
Ok(Some(Self::Child(child_name)))
}
Some('[') => {
chars.next();
let mut index_str = String::new();
loop {
match chars.peek() {
Some(']') => {
chars.next();
break;
}
Some(c) => {
index_str.push(*c);
chars.next();
}
None => {
return Err(HiveGetJsonObjectError::InvalidJsonPath);
}
}
}
if index_str.is_empty() || index_str == "*" {
return Ok(Some(Self::SubscriptAll));
}
let index = str::parse::<usize>(&index_str)
.map_err(|_| HiveGetJsonObjectError::InvalidJsonPath)?;
Ok(Some(Self::Subscript(index)))
}
Some(_) => Err(HiveGetJsonObjectError::InvalidJsonPath),
}
}
fn evaluate_serde_json<'a>(&self, value: &'a serde_json::Value) -> Cow<'a, serde_json::Value> {
match self {
HiveGetJsonObjectMatcher::Root => {
return Cow::Borrowed(value);
}
HiveGetJsonObjectMatcher::Child(child) => {
if let serde_json::Value::Object(object) = value {
return match object.get(child) {
Some(child) => Cow::Borrowed(child),
None => Cow::Owned(serde_json::Value::Null),
};
} else if let serde_json::Value::Array(array) = value {
let vs = array
.iter()
.map(|item| {
if let serde_json::Value::Object(object) = item {
match object.get(child) {
Some(v) => v.clone(),
None => serde_json::Value::Null,
}
} else {
serde_json::Value::Null
}
})
.filter(|r| !r.is_null())
.flat_map(|r| {
// keep consistent with hive UDFJson
let iter: Box<dyn Iterator<Item = serde_json::Value>> = match r {
serde_json::Value::Array(array) => Box::new(array.into_iter()),
other => Box::new(std::iter::once(other)),
};
iter
})
.collect::<Vec<_>>();
if vs.is_empty() {
return Cow::Owned(serde_json::Value::Null);
}
return Cow::Owned(serde_json::Value::Array(vs));
}
}
HiveGetJsonObjectMatcher::Subscript(index) => {
if let serde_json::Value::Array(array) = value {
return match array.get(*index) {
Some(v) => Cow::Borrowed(v),
None => Cow::Owned(serde_json::Value::Null),
};
}
}
HiveGetJsonObjectMatcher::SubscriptAll => {
if let serde_json::Value::Array(_) = value {
return Cow::Borrowed(value);
}
}
}
Cow::Owned(serde_json::Value::Null)
}
fn evaluate_sonic<'a>(&self, value: &'a sonic_rs::Value) -> Cow<'a, sonic_rs::Value> {
match self {
HiveGetJsonObjectMatcher::Root => {
return Cow::Borrowed(value);
}
HiveGetJsonObjectMatcher::Child(child) => {
if let Some(object) = value.as_object() {
return match object.get(child) {
Some(child) => Cow::Borrowed(child),
None => Cow::Owned(sonic_rs::Value::default()),
};
} else if let Some(array) = value.as_array() {
let vs = array
.into_iter()
.map(|item| {
if let Some(object) = item.as_object() {
match object.get(child) {
Some(v) => v.clone(),
None => sonic_rs::Value::default(),
}
} else {
sonic_rs::Value::default()
}
})
.filter(|r| !r.is_null())
.flat_map(|r| {
// keep consistent with hive UDFJson
let iter: Box<dyn Iterator<Item = sonic_rs::Value>> = match r {
v if v.is_array() => Box::new(
v.into_array().expect("expected array value").into_iter(),
),
other => Box::new(std::iter::once(other)),
};
iter
})
.collect::<Vec<_>>();
if vs.is_empty() {
return Cow::Owned(sonic_rs::Value::default());
}
return Cow::Owned(sonic_rs::Value::from(vs));
}
}
HiveGetJsonObjectMatcher::Subscript(index) => {
if let Some(array) = value.as_array() {
return match array.get(*index) {
Some(v) => Cow::Borrowed(v),
None => Cow::Owned(sonic_rs::Value::default()),
};
}
}
HiveGetJsonObjectMatcher::SubscriptAll => {
if let Some(_array) = value.as_array() {
return Cow::Borrowed(value);
}
}
}
Cow::Owned(sonic_rs::Value::default())
}
}
#[cfg(test)]
mod test {
use std::{error::Error, sync::Arc};
use arrow::array::{AsArray, StringArray};
use datafusion::{common::ScalarValue, logical_expr::ColumnarValue};
use crate::spark_get_json_object::{
HiveGetJsonObjectEvaluator, spark_get_parsed_json_object, spark_parse_json,
};
#[test]
fn test_hive_demo() -> Result<(), Box<dyn Error>> {
let input = r#"
{
"store": {
"fruit": [
{
"weight": 8,
"type": "apple"
},
{
"weight": 9,
"type": "pear"
}
],
"bicycle": {
"price": 19.95,
"color": "red"
}
},
"email": "amy@only_for_json_udf_test.net",
"owner": "amy"
}"#;
let path = "$.owner";
assert_eq!(
HiveGetJsonObjectEvaluator::try_new(path)
.expect("failed to create evaluator")
.evaluate(input)
.expect("evaluation failed"),
Some("amy".to_owned())
);
let path = "$. owner";
assert_eq!(
HiveGetJsonObjectEvaluator::try_new(path)
.expect("failed to create evaluator")
.evaluate(input)
.expect("evaluation failed"),
Some("amy".to_owned())
);
let path = "$.store.bicycle.price";
assert_eq!(
HiveGetJsonObjectEvaluator::try_new(path)
.expect("failed to create evaluator")
.evaluate(input)
.expect("evaluation failed"),
Some("19.95".to_owned())
);
let path = "$. store. bicycle. price";
assert_eq!(
HiveGetJsonObjectEvaluator::try_new(path)
.expect("failed to create evaluator")
.evaluate(input)
.expect("evaluation failed"),
Some("19.95".to_owned())
);
let path = "$.store.fruit[0]";
assert_eq!(
HiveGetJsonObjectEvaluator::try_new(path)
.expect("failed to create evaluator")
.evaluate(input)
.expect("evaluation failed"),
Some(r#"{"weight":8,"type":"apple"}"#.to_owned())
);
let path = "$. store. fruit[0]";
assert_eq!(
HiveGetJsonObjectEvaluator::try_new(path)
.expect("failed to create evaluator")
.evaluate(input)
.expect("evaluation failed"),
Some(r#"{"weight":8,"type":"apple"}"#.to_owned())
);
let path = "$.store.fruit[1].weight";
assert_eq!(
HiveGetJsonObjectEvaluator::try_new(path)
.expect("failed to create evaluator")
.evaluate(input)
.expect("evaluation failed"),
Some("9".to_owned())
);
let path = "$.store.fruit[*]";
assert_eq!(
HiveGetJsonObjectEvaluator::try_new(path)
.expect("failed to create evaluator")
.evaluate(input)
.expect("evaluation failed"),
Some(r#"[{"weight":8,"type":"apple"},{"weight":9,"type":"pear"}]"#.to_owned())
);
let path = "$. store. fruit[*]";
assert_eq!(
HiveGetJsonObjectEvaluator::try_new(path)
.expect("failed to create evaluator")
.evaluate(input)
.expect("evaluation failed"),
Some(r#"[{"weight":8,"type":"apple"},{"weight":9,"type":"pear"}]"#.to_owned())
);
let path = "$.store.fruit.[1].type";
assert_eq!(
HiveGetJsonObjectEvaluator::try_new(path)
.expect("failed to create evaluator")
.evaluate(input)
.expect("evaluation failed"),
Some("pear".to_owned())
);
let path = "$. store. fruit. [1]. type";
assert_eq!(
HiveGetJsonObjectEvaluator::try_new(path)
.expect("failed to create evaluator")
.evaluate(input)
.expect("evaluation failed"),
Some("pear".to_owned())
);
let path = "$.non_exist_key";
assert_eq!(
HiveGetJsonObjectEvaluator::try_new(path)
.expect("failed to create evaluator")
.evaluate(input)
.expect("evaluation failed"),
None
);
Ok(())
}
#[test]
fn test_2() -> Result<(), Box<dyn Error>> {
let input = r#"
{
"ID": 121,
"message": {
"name": "
非法换行
非法转义\哈
特殊字符😍
Asher
",
"location": [
{
"county": "浦东",
"city": "1.234"
},
{
"county": "西直门",
"city": 1.234
},
{
"other": "invalid"
}
]
}
}"#;
let input_array = Arc::new(StringArray::from(vec![input]));
let parsed = spark_parse_json(&[ColumnarValue::Array(input_array)])?;
let path = ColumnarValue::Scalar(ScalarValue::from("$.message.location.county"));
let r = spark_get_parsed_json_object(&[parsed.clone(), path])?.into_array(1)?;
let v = r
.as_string::<i32>()
.iter()
.next()
.expect("missing first element");
assert_eq!(v, Some(r#"["浦东","西直门"]"#));
let path = ColumnarValue::Scalar(ScalarValue::from("$.message.location.NOT_EXISTED"));
let r = spark_get_parsed_json_object(&[parsed.clone(), path])?.into_array(1)?;
let v = r
.as_string::<i32>()
.iter()
.next()
.expect("missing first element");
assert_eq!(v, None);
let path = ColumnarValue::Scalar(ScalarValue::from("$.message.name"));
let r = spark_get_parsed_json_object(&[parsed.clone(), path])?.into_array(1)?;
let v = r
.as_string::<i32>()
.iter()
.next()
.expect("missing first element")
.ok_or("value is NULL")?;
assert!(v.contains("Asher"));
let path = ColumnarValue::Scalar(ScalarValue::from("$.message.location.city"));
let r = spark_get_parsed_json_object(&[parsed.clone(), path])?.into_array(1)?;
let v = r
.as_string::<i32>()
.iter()
.next()
.expect("missing first element");
assert_eq!(v, Some(r#"["1.234",1.234]"#));
let path = ColumnarValue::Scalar(ScalarValue::from("$.message.location[0]"));
let r = spark_get_parsed_json_object(&[parsed.clone(), path])?.into_array(1)?;
let v = r
.as_string::<i32>()
.iter()
.next()
.expect("missing first element");
assert_eq!(v, Some(r#"{"city":"1.234","county":"浦东"}"#));
let path = ColumnarValue::Scalar(ScalarValue::from("$.message.location[].county"));
let r = spark_get_parsed_json_object(&[parsed.clone(), path])?.into_array(1)?;
let v = r
.as_string::<i32>()
.iter()
.next()
.expect("missing first element");
assert_eq!(v, Some(r#"["浦东","西直门"]"#));
Ok(())
}
#[test]
fn test_3() -> Result<(), Box<dyn Error>> {
let input = r#"
{
"i1": [
{
"j1": 100,
"j2": [
200,
300
]
}, {
"j1": 300,
"j2": [
400,
500
]
}, {
"j1": 300,
"j2": null
}, {
"j1": 300,
"j2": "other"
}
]
}
"#;
let input_array = Arc::new(StringArray::from(vec![input]));
let parsed = spark_parse_json(&[ColumnarValue::Array(input_array)])?;
let path = ColumnarValue::Scalar(ScalarValue::from("$.i1.j2"));
let r = spark_get_parsed_json_object(&[parsed.clone(), path])?.into_array(1)?;
let v = r
.as_string::<i32>()
.iter()
.next()
.expect("missing first element");
// NOTE:
// standard jsonpath should output [[200,300],[400, 500],null,"other"]
// but we have to keep consistent with hive UDFJson
assert_eq!(v, Some(r#"[200,300,400,500,"other"]"#));
Ok(())
}
}