Search code examples
jsonrustserde

How to modify an array in a json file?


I'm trying to learn rust by writing a simple accounting program. I decided to store data as a json file locally. So the json file structure is like this:

{
    "wallet": "wallet1",
    "balance": 2000.0,
    "transactions": [
        {
            "tr_type": "Withdraw",
            "date": 20230522,
            "volume": 300.0
        },
        {
            "tr_type": "Deposit",
            "date": 20230422,
            "volume": 150.0
        },
        {
            "tr_type": "Withdraw",
            "date": 20230222,
            "volume": 300.0
        },
        {
            "tr_type": "Deposit",
            "date": 20230707,
            "volume": 200.0
        }
    ]
}

In my program, I want to read this information from the file, do some calculations and show the results to the user. Also take new transaction input from user and add it to the json file. I came up with the code below (the user-input transaction is hardcoded as new_transaction):

use serde::{Deserialize, Serialize};
use std::fs::OpenOptions;
use std::io::prelude::*;
// use std::io::{BufWriter, Write};

#[derive(Serialize, Deserialize, Debug)]
struct Transaction {
    tr_type: TRtype,
    date: u64,
    volume: f32,
}

#[derive(Serialize, Deserialize, Debug)]
struct Account {
    wallet: String,
    balance: f32,
    transactions: Vec<Transaction>,
}

#[derive(Serialize, Deserialize, Debug)]
enum TRtype {
    Deposit,
    Withdraw,
}

fn main() -> std::io::Result<()> {

    let mut myfile = OpenOptions::new()
        .read(true)
        .write(true)
        .create(true)
        .open("wallets.json")?;

    let mut raw_json = String::new();
    myfile.read_to_string(&mut raw_json)?;

    let mut deserialized: Account = serde_json::from_str(&raw_json)?;
    for trx in &deserialized.transactions {
        println!(
            "transaction: {:?}, volume = {}, date = {}",
            trx.tr_type, trx.volume, trx.date
        );
    }

    let new_transaction: Transaction = Transaction {
        tr_type: TRtype::Deposit,
        date: 20230707,
        volume: 1500.0,
    };

    deserialized.transactions.push(new_transaction);

    let serialized = serde_json::to_string(&deserialized).unwrap();

    myfile.rewind().unwrap();
    myfile.write_all(serialized.as_bytes())?;

    Ok(())
}

This doesn't seem so elegant. For every new transaction the file will be overwritten completely.

So, is there a way to access the transaction array in the file and append to it without touching other lines?


Solution

  • According to this python StackOverflow post.

    It is not possible to partially update a JSON file.

    How to update a JSON file by using Python?

    As @ijustlovemath pointed, maybe is better to use a NoSQL database like MongoDB or SQL database MySQL , PostgreSQL , SQLite.