2

If I have a JSON value with unknown layout I can deserialise it with serde_json using serde_json::Value:

#[derive(Deserialize)]
struct Foo {
  unknown: serde_json::Value,
}

Similarly I can do the same with CBOR:

#[derive(Deserialize)]
struct Foo {
  unknown: serde_cbor::Value,
}

But what if I want a single data structure that can be loaded from JSON or CBOR. I effectively want this:

enum UnknownValue {
  Json(serde_json::Value),
  Cbor(serde_cbor::Value),
}

#[derive(Deserialize)]
struct Foo {
  unknown: UnknownValue,
}

Is there any way to do this so that I can deserialise JSON or CBOR into this struct?

Timmmm
  • 88,195
  • 71
  • 364
  • 509
  • use untagged there must be a question somewhere about it – Stargateur Oct 02 '20 at 10:29
  • Does this answer your question? [how to parse a YAML containing a simple list together with a key-value list (associative array)](https://stackoverflow.com/questions/61793475/how-to-parse-a-yaml-containing-a-simple-list-together-with-a-key-value-list-ass) – Stargateur Oct 02 '20 at 10:31
  • or https://stackoverflow.com/questions/52034764/how-do-i-serialize-an-enum-without-including-the-name-of-the-enum-variant/52035083#52035083 pick one – Stargateur Oct 02 '20 at 10:32
  • @Stargateur I don't know that these applies, `serde(untagged)` is for deserialising "proper" rust-level enums, but here what they want is to deserialise generic untyped / unstructured values from two completely different serialisation libraries (json and cbor). Since cbor datatypes look to be a superset of json's I'd suggest just converting `serde_json::Value` to `serde_cbor::Value` and always working with the latter. Alternatively if converting back is necessary you may want to do the opposite (restrict valid cbor input to json-compatible). – Masklinn Oct 02 '20 at 11:09

1 Answers1

2

Ok, I tried serde(untagged), and it sort of works, except that it always results in UnknownValue::Json, even if you deserialise using serde_cbor. I'm not sure how that works exactly:

use serde::Deserialize;

#[derive(Deserialize, Debug)]
#[serde(untagged)]
enum UnknownValue {
    Json(serde_json::Value),
    Cbor(serde_cbor::Value),
}

#[derive(Deserialize, Debug)]
struct Foo {
    unknown: UnknownValue,
}

fn main() {
    let json_input = br#"{"unknown": 23}"#;
    let cbor_input = [0xA1, 0x67, 0x75, 0x6E, 0x6B, 0x6E, 0x6F, 0x77, 0x6E, 0x17];

    let j: Foo = serde_json::from_slice(json_input).unwrap();
    let c: Foo = serde_cbor::from_slice(&cbor_input).unwrap();
    dbg!(j);
    dbg!(c);
}

Prints

[src/main.rs:21] j = Foo {
    unknown: Number(
        23,
    ),
}
[src/main.rs:22] c = Foo {
    unknown: Number(
        23,
    ),
}

So I wondered if maybe it just works if you use serde_json::Value and forget the enum, and lo, it does! Using this instead gives exactly the same output:

#[derive(Deserialize, Debug)]
struct Foo {
    unknown: serde_json::Value,
}

So this doesn't answer my actual question (e.g. if you want to get CBOR tags), but it does satisfy my use case. Probably also worth mentioning serde-value which seems designed for this sort of thing, though I haven't tried it.

Timmmm
  • 88,195
  • 71
  • 364
  • 509
  • well if as masklinn said cbor is a superset of json you must put it in first, serde untagged accept the first match. – Stargateur Oct 02 '20 at 18:35