From Python To Rust Pt. 1: Bulk JSON File Edits
After managing large a few large projects in Python, you may start having the desire to investigate more strongly typed languages, such as Golang or Rust. I’ve written a few small utilities in Go, and they work well in our design engineering automation workflow. Before committing to Go, I decided to learn the basics of Rust as well.
The goal is to create a series of posts where I’ll build small utilities in Rust and Python to compare and contrast the two languages for (my) real-world use cases.
Rust faired extremely well compared to Python in my first exploration from a development standpoint. The level of abstraction is surprisingly similar for this particular task. Python wound up with shorter expressions per line, whereas Rust is more verbose per expression. I was pleasantly surprised that at no time did I have to reinvent the wheel for basic functionality with Rust on this exercise, a situation I ran into with Go semi-frequently.
The convenience factor most missed from Python in this example is the wildcard pattern matching from the glob.glob() function that automatically filters out all files that do not end in '.json'. The Rust function Path::new(&filepath).extension() returns None for .DS_Store files and, therefore, must be checked to eliminate a runtime panic when running this utility on macOS machines.
Python JSON file editor:
import os
import glob
import json
from pathlib import Path
def main():
# ensure output directory exists
output_dir = "/Users/bradywalters/reference_files_outputs"
if not os.path.isdir(output_dir):
os.mkdir(output_dir)
# walk only json files in pickup folder
for filepath in glob.glob("/Users/bradywalters/reference_files/*.json"):
# load json file into memory & add new field
with open(filepath) as json_file:
data_dict = json.load(json_file)
data_dict["Python_Added_Field"] = "Added By Python"
# save data back to json file in output directory
filename = Path(filepath).name
output_filepath = Path(output_dir, filename).as_posix()
json_data = json.dumps(data_dict, indent=2)
with open(output_filepath, 'w') as file:
file.write(json_data)
if __name__ == '__main__':
main()
Runs JSON file editor
use serde_json::Value;
use std::{fs, path::Path};
fn main() {
// ensure output directory exists
let output_dir = "/Users/bradywalters/reference_files_outputs";
if !Path::new(&output_dir).is_dir() {
fs::create_dir(output_dir).expect("unable to create output dir");
}
// walk only json files in pickup folder
for file in fs::read_dir("/Users/bradywalters/reference_files").unwrap() {
// ignore non .json files, this is handled by pattern matting in glob.glob in python
let filepath = file.unwrap().path();
let file_extension = Path::new(&filepath).extension();
if file_extension.is_none() || file_extension.unwrap() != "json" {
continue;
}
//load json file into memory & add new field
let data = fs::read_to_string(&filepath).expect("Unable to read file");
let mut json: serde_json::Value = serde_json::from_str(&data).expect("Bad JSON format");
json["Rust_Added_Field"] = Value::String(String::from("Added by Rust"));
// save data back to json file in output directory
let filename = Path::new(&filepath).file_name().unwrap().to_str().unwrap();
let output_filepath = format!("{}/{}", output_dir, filename);
fs::write(
output_filepath,
serde_json::to_string_pretty(&json).unwrap(),
)
.unwrap()
}
}
Subscribe to my newsletter
Read articles from Brady Waters directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by