Reputation: 4042
I am using serde_json to deserialise a json document. I have a function that given a string (this is the json document), will return a serde_json Value (this is an enum that represents the json type), returns an Option. This value is passed around to other functions as required.
However, I realised that passing around a Value is not quite what I want, because doing this, the key is not available.
To illustrate my point, if I have a json document that looks like this:
{
"root" : {
"regex" : null,
"prefixes" : [ "a_", "b_" ]
}
}
"root" is a json object, "regex" is json Null and "prefixes" is a json array.
Now, the json type Value is an enum with discriminators representing the json types, eg Object, Null, Array for the examples given above.
The serde_json crate uses std::collections::BTreeMap to represent nodes in the json document, where the String type repesents the json keys (in the above, these would be "root", "regex" and "prefixes". So passing around just references to Values is only partly helpful, I should be passing around BTreeMap instead, so that I can access the key too.
So this is the following function that I am trying to re-write:
fn get_json_content(content_s : &str) -> Option<Value> {
// instead of returning a value, we need to return a BTreeMap, so we can get the
// key and the value.
println!("===>>> json_content obtained: {}", content_s);
match serde_json::from_str(content_s) { // -> Result<Value>
Ok(some_value) => Some(some_value),
Err(_) => None
}
}
So I started to re-write the function but became up against the "the type of this value must be known in this context" error:
fn get_json_content_as_btreemap<'a>(content_s : &str) -> Option<&'a BTreeMap<String, Value>> {
match serde_json::from_str(content_s) { // -> Result<Value>
Ok(some) => {
// I expect the type of key_value_pair to be BTreeMap<String, Value>>
// (but I may be wrong!)
let key_value_pair = some.as_object().unwrap(); // Error here
},
Err(_) => None
}
}
I found other questions on stackoverflow like this one: the type of this value must be known in this context
and using this as a helper, I tried to insert the type as follows:
let key_value_pair = some.as_object::<BTreeMap<_, _>>().unwrap();
which doesnt fix the issue. Also, tried other similar variations to no avail. So how do I fix this please?
EDIT:
I have another function in this app as follows:
fn get_root_value<'a>(json_documemt : &'a Value) -> Result<&'a Value, JsonErrorCode> {
if json_documemt.is_object() {
for (k, v) in json_documemt.as_object().unwrap().iter() {
if k == "root" {
println!("found root: {}", k);
return Ok(v)
}
}
return Err(JsonErrorCode::Custom("Failed to find root node".to_string()))
}
Err(JsonErrorCode::Custom("Not an object".to_string()))
}
... and this works fine. Here you can see that I can call as_object() and then obtain the key and value as a tuple pair. I don't understand why as_object is working in one case but not the other. I would like to pull out the BTreeMap and pass this around as a borrowed item.
Upvotes: 2
Views: 748
Reputation: 7561
You can change the return type of your initial function and serde_json
will deserialize to the appropriate object if it can:
fn get_json_content(content_s : &str) -> Option<BTreeMap<String, Value>> {
// instead of returning a value, we need to return a BTreeMap, so we can get the
// key and the value.
println!("===>>> json_content obtained: {}", content_s);
match serde_json::from_str(content_s) { // -> Result<Value>
Ok(some_value) => Some(some_value),
Err(_) => None
}
// Note: this match statement can be rewritten as
// serde_json::from_str(content_s).ok()
}
Your second example won't work because you are instantiating the Value
object inside the function, and then trying to return a reference to the object you just instantiated. This won't work because the object will go out of scope at the end of the function and the reference will then be invalid.
Upvotes: 0