最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

rust - Stumped with serde_json map - Stack Overflow

programmeradmin2浏览0评论

I am trying to generate json object as below

// final json output expected
{
    "j1": {
        "1981": {
            "income": 215
        },
        "1982": {
            "income": 350
        },
    },
    "j2": {
        "1981": {
            "income": 100
        },
        "1982": {
            "income": 215
        },
    }
}

I have the following code

use serde_json::{Value, Map};

struct s {
    name: String,
    year: i32,
    income: i64
}

fn main() {
    let rows :Vec<s> = vec!(
        s { name: "j1".to_string(), year: 1981, income: 100},
        s { name: "j1".to_string(), year: 1981, income: 115},
        s { name: "j1".to_string(), year: 1982, income: 120},
        s { name: "j1".to_string(), year: 1982, income: 100},
        s { name: "j1".to_string(), year: 1982, income: 130},
        s { name: "j2".to_string(), year: 1981, income: 100},
        s { name: "j2".to_string(), year: 1982, income: 120},
        s { name: "j2".to_string(), year: 1982, income: 130}
    );
    
    let mut data_map: Map<String, Value> = Map::new();
    for row in rows {
        let  mut name: &Map<String, Value> = match data_map.get(&row.name) {
            Some(val) => { &val.as_object().unwrap() },
            None => { &Map::new() }
        };
        let mut year:  &Map<String, Value> = match data_map.get(&row.year.to_string()) {
            Some(val) => { &val.as_object().unwrap() },
            None => { &Map::new() }
        };
        let income: i64 = year.get("income").unwrap_or(&Value::Number(0i64.into())).as_i64().unwrap() + row.income;
        year.insert("income".to_string(), Value::Number(income.into()));
    }
}

I created this code to ask the question. I also consulted with deepseek and such

Error message that I am getting is

error[E0596]: cannot borrow `*year` as mutable, as it is behind a `&` reference
  --> src\main.rs:32:9
   |
32 |         year.insert("income".to_string(), Value::Number(income.into()));
   |         ^^^^ `year` is a `&` reference, so the data it refers to cannot be borrowed as mutable
   |
help: consider changing this binding's type
   |
27 |         let mut year: &mut serde_json::Map<std::string::String, Value> = match data_map.get(&row.year.to_string()) {
   |                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Clearly I have deficiency in understanding reference and borrowing. Any help would be appreciated.

I am trying to generate json object as below

// final json output expected
{
    "j1": {
        "1981": {
            "income": 215
        },
        "1982": {
            "income": 350
        },
    },
    "j2": {
        "1981": {
            "income": 100
        },
        "1982": {
            "income": 215
        },
    }
}

I have the following code

use serde_json::{Value, Map};

struct s {
    name: String,
    year: i32,
    income: i64
}

fn main() {
    let rows :Vec<s> = vec!(
        s { name: "j1".to_string(), year: 1981, income: 100},
        s { name: "j1".to_string(), year: 1981, income: 115},
        s { name: "j1".to_string(), year: 1982, income: 120},
        s { name: "j1".to_string(), year: 1982, income: 100},
        s { name: "j1".to_string(), year: 1982, income: 130},
        s { name: "j2".to_string(), year: 1981, income: 100},
        s { name: "j2".to_string(), year: 1982, income: 120},
        s { name: "j2".to_string(), year: 1982, income: 130}
    );
    
    let mut data_map: Map<String, Value> = Map::new();
    for row in rows {
        let  mut name: &Map<String, Value> = match data_map.get(&row.name) {
            Some(val) => { &val.as_object().unwrap() },
            None => { &Map::new() }
        };
        let mut year:  &Map<String, Value> = match data_map.get(&row.year.to_string()) {
            Some(val) => { &val.as_object().unwrap() },
            None => { &Map::new() }
        };
        let income: i64 = year.get("income").unwrap_or(&Value::Number(0i64.into())).as_i64().unwrap() + row.income;
        year.insert("income".to_string(), Value::Number(income.into()));
    }
}

I created this code to ask the question. I also consulted with deepseek and such

Error message that I am getting is

error[E0596]: cannot borrow `*year` as mutable, as it is behind a `&` reference
  --> src\main.rs:32:9
   |
32 |         year.insert("income".to_string(), Value::Number(income.into()));
   |         ^^^^ `year` is a `&` reference, so the data it refers to cannot be borrowed as mutable
   |
help: consider changing this binding's type
   |
27 |         let mut year: &mut serde_json::Map<std::string::String, Value> = match data_map.get(&row.year.to_string()) {
   |                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Clearly I have deficiency in understanding reference and borrowing. Any help would be appreciated.

Share Improve this question edited 2 days ago Popeye asked Feb 7 at 23:25 PopeyePopeye 3803 silver badges17 bronze badges 2
  • 1 Could you try minimizing your example (that is, getting rid of the code which is not needed to reproduce the error) and attach the full error from cargo check (not the one from IDE)? All information is already here, but it could be much more accessible. – Cerberus Commented Feb 8 at 5:01
  • Your JSON has the wrong numbers if you intend to have the sum of all values. – Finomnis Commented 2 days ago
Add a comment  | 

1 Answer 1

Reset to default 1

First off, you don't usually create JSON by hand. You create a Rust representation of it, and then let serde_json convert it to JSON. That means, don't deal with Value yourself.

You can of course manually construct JSONs as well, but it rarely has an advantage over letting serde_json do that.

Here's a solution. First, we need the following dependencies in Cargo.toml:

[dependencies]
serde = { version = "1.0.217", features = ["derive"] }
serde_json = "1.0.138"

And here is the code:

use serde::{Deserialize, Serialize};

use std::collections::HashMap;

struct S {
    name: String,
    year: i32,
    income: i64,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
struct YearData {
    income: i64,
}

fn main() {
    let rows: Vec<S> = vec![
        S { name: "j1".to_string(), year: 1981, income: 100 },
        S { name: "j1".to_string(), year: 1981, income: 115 },
        S { name: "j1".to_string(), year: 1982, income: 120 },
        S { name: "j1".to_string(), year: 1982, income: 100 },
        S { name: "j1".to_string(), year: 1982, income: 130 },
        S { name: "j2".to_string(), year: 1981, income: 100 },
        S { name: "j2".to_string(), year: 1982, income: 120 },
        S { name: "j2".to_string(), year: 1982, income: 130 },
    ];

    let mut data_map: HashMap<String, HashMap<String, YearData>> = HashMap::new();
    for row in rows {
        data_map
            .entry(row.name)
            .or_default()
            .entry(row.year.to_string())
            .or_insert(YearData { income: 0 })
            .income += row.income;
    }

    let serde_string = serde_json::to_string_pretty(&data_map).unwrap();

    println!("{}", serde_string);
}
{
  "j1": {
    "1982": {
      "income": 350
    },
    "1981": {
      "income": 215
    }
  },
  "j2": {
    "1981": {
      "income": 100
    },
    "1982": {
      "income": 250
    }
  }
}

Regarding the borrowing error in your original code: I'm uncertain how to help you there. There are many errors in there, both ownership issues as also logical issues.

I assume that you intend to query a member from data_map by using the row.name key, and if it doesn't exist, insert a new map. I see that you create a new map, but you never insert it into data_map anywhere. The rest of the issues are a bunch of & that are wrong, sometimes because the values are already references, and sometimes because they are towards variables that are temporary and you create dangling references.

I'd love to dissect your code in more detail and explain the errors to you, but it's kind of too confusing to do so :D

Instead, here's a rewrite. Maybe it helps your understanding:

use serde_json::{Map, Value};

struct S {
    name: String,
    year: i32,
    income: i64,
}

fn main() {
    let rows: Vec<S> = vec![
        S { name: "j1".to_string(), year: 1981, income: 100 },
        S { name: "j1".to_string(), year: 1981, income: 115 },
        S { name: "j1".to_string(), year: 1982, income: 120 },
        S { name: "j1".to_string(), year: 1982, income: 100 },
        S { name: "j1".to_string(), year: 1982, income: 130 },
        S { name: "j2".to_string(), year: 1981, income: 100 },
        S { name: "j2".to_string(), year: 1982, income: 120 },
        S { name: "j2".to_string(), year: 1982, income: 130 },
    ];

    let mut data_map: Map<String, Value> = Map::new();
    for row in rows {
        let name: &mut Map<String, Value> = data_map
            .entry(row.name)
            .or_insert_with(|| Map::new().into())
            .as_object_mut()
            .unwrap();
        let year: &mut Map<String, Value> = name
            .entry(row.year.to_string())
            .or_insert_with(|| Map::new().into())
            .as_object_mut()
            .unwrap();
        let income: i64 = year
            .get("income")
            .unwrap_or(&Value::Number(0i64.into()))
            .as_i64()
            .unwrap()
            + row.income;
        year.insert("income".to_string(), Value::Number(income.into()));
    }

    println!("{}", serde_json::to_string_pretty(&data_map).unwrap());
}
{
  "j1": {
    "1981": {
      "income": 215
    },
    "1982": {
      "income": 350
    }
  },
  "j2": {
    "1981": {
      "income": 100
    },
    "1982": {
      "income": 250
    }
  }
}

But again, don't do this unless absolutely necessary; serde_json is not really meant to be used like this. Using Value directly just causes a lot of as_<type>().unwrap() casts, and there's no real advantage over using a Rust type that implements serde::Serialize. This is how serde_json is meant to be used; see #[derive(Serialize)] in my first example. I recommend reading serde.rs for more information.

发布评论

评论列表(0)

  1. 暂无评论