Skip to content

Commit

Permalink
Merge pull request #208 from kylebarron/kyle/write-json
Browse files Browse the repository at this point in the history
Handle nested properties in GeoJSON reader and writer
  • Loading branch information
michaelkirk authored May 14, 2024
2 parents ecd081b + b374119 commit 05daf37
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 11 deletions.
30 changes: 20 additions & 10 deletions geozero/src/geojson/geojson_reader.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::error::Result;
use crate::error::{GeozeroError, Result};
use crate::{
ColumnValue, FeatureProcessor, GeomProcessor, GeozeroDatasource, GeozeroGeometry,
PropertyProcessor,
Expand Down Expand Up @@ -207,20 +207,30 @@ pub(crate) fn process_properties<P: PropertyProcessor>(
// Could we provide a stable property index?
match value {
JsonValue::String(v) => processor.property(i, key, &ColumnValue::String(v))?,
JsonValue::Number(v) if v.is_f64() => {
processor.property(i, key, &ColumnValue::Double(v.as_f64().unwrap()))?
JsonValue::Number(v) => {
if v.is_f64() {
processor.property(i, key, &ColumnValue::Double(v.as_f64().unwrap()))?
} else if v.is_i64() {
processor.property(i, key, &ColumnValue::Long(v.as_i64().unwrap()))?
} else if v.is_u64() {
processor.property(i, key, &ColumnValue::ULong(v.as_u64().unwrap()))?
} else {
unreachable!()
}
}
JsonValue::Number(v) if v.is_i64() => {
processor.property(i, key, &ColumnValue::Long(v.as_i64().unwrap()))?
JsonValue::Bool(v) => processor.property(i, key, &ColumnValue::Bool(*v))?,
JsonValue::Array(v) => {
let json_string =
serde_json::to_string(v).map_err(|_err| GeozeroError::Property(key.clone()))?;
processor.property(i, key, &ColumnValue::Json(&json_string))?
}
JsonValue::Number(v) if v.is_u64() => {
processor.property(i, key, &ColumnValue::ULong(v.as_u64().unwrap()))?
JsonValue::Object(v) => {
let json_string =
serde_json::to_string(v).map_err(|_err| GeozeroError::Property(key.clone()))?;
processor.property(i, key, &ColumnValue::Json(&json_string))?
}
JsonValue::Bool(v) => processor.property(i, key, &ColumnValue::Bool(*v))?,
// For null values omit the property
JsonValue::Null => false,
// Array(Vec<Value>), Object(Map<String, Value>)
_ => processor.property(i, key, &ColumnValue::String(&value.to_string()))?,
};
}
Ok(())
Expand Down
61 changes: 60 additions & 1 deletion geozero/src/geojson/geojson_writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,12 @@ fn write_str_prop<W: Write>(mut out: W, colname: &str, v: &str) -> Result<()> {
Ok(())
}

fn write_json_prop<W: Write>(mut out: W, colname: &str, v: &str) -> Result<()> {
let colname = colname.replace('\"', "\\\"");
out.write_all(format!(r#""{colname}": {v}"#).as_bytes())?;
Ok(())
}

impl<W: Write> PropertyProcessor for GeoJsonWriter<W> {
fn property(&mut self, i: usize, colname: &str, colval: &ColumnValue) -> Result<bool> {
if i > 0 {
Expand All @@ -228,7 +234,7 @@ impl<W: Write> PropertyProcessor for GeoJsonWriter<W> {
ColumnValue::String(v) | ColumnValue::DateTime(v) => {
write_str_prop(&mut self.out, colname, v)?;
}
ColumnValue::Json(_v) => (),
ColumnValue::Json(v) => write_json_prop(&mut self.out, colname, v)?,
ColumnValue::Binary(_v) => (),
};
Ok(false)
Expand Down Expand Up @@ -438,6 +444,59 @@ mod test {
)
}

#[test]
fn nested_object_property() {
let geojson = r#"{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"id": "NZL",
"name": "New Zealand",
"nested": {
"a": 1,
"b": true
}
},
"geometry": {
"type": "Point",
"coordinates": [-80, 40]
}
}
]
}
"#;
let mut out: Vec<u8> = Vec::new();
assert!(read_geojson(geojson.as_bytes(), &mut GeoJsonWriter::new(&mut out)).is_ok());
assert_json_eq(&out, geojson);
}

#[test]
fn nested_array_property() {
let geojson = r#"{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"id": "NZL",
"name": "New Zealand",
"nested": [1, 2, 3, 4]
},
"geometry": {
"type": "Point",
"coordinates": [-80, 40]
}
}
]
}
"#;
let mut out: Vec<u8> = Vec::new();
assert!(read_geojson(geojson.as_bytes(), &mut GeoJsonWriter::new(&mut out)).is_ok());
assert_json_eq(&out, geojson);
}

fn assert_json_eq(a: &[u8], b: &str) {
let a = std::str::from_utf8(a).unwrap();
let a: serde_json::Value = serde_json::from_str(a).unwrap();
Expand Down

0 comments on commit 05daf37

Please sign in to comment.