From Python To Rust Pt. 1: Bulk JSON File Edits

Brady WatersBrady Waters
2 min read

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()
    }
}
0
Subscribe to my newsletter

Read articles from Brady Waters directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Brady Waters
Brady Waters