Parsing enum with adjacent tag in vec/tuple

Hello There,
I am currently parsing JSON from a SingalR WebSocket with serde and serde_json.
But I am unable to correctly parse the Update Messages.
Here is a minimal playground with two types up update/messages.

I have an array which is the update.

["TimingData",{"td": "is important"}, "timestamp"]

The first element says which topic gets updated.
The second element is the update.
The this is a timestamp.

This is the code I tried (and can be found in the playground)

#[derive(Deserialize, Debug)]
struct Heartbeat {
    beep: bool,
}
#[derive(Deserialize, Debug)]
struct TimingData {
    td: String,
}

#[derive(Deserialize, Debug)]
enum Update {
    Heartbeat(Heartbeat),
    TimingData(TimingData),
}

#[derive(Deserialize, Debug)]
struct Message {
    pub a: (String, Update, String),
}

I know of the tagging and representation of enums.
But when I use untagged, I get miss matches of the variant. Because the data structure starts the same as another topic but doesn't end the same and serde takes the wrong one.

I think I need something like "Adjacently tagged enums", but in an vec or tuple. Also I need the timestamp, it can't be ignored.

I saw a serde GitHub issue which look similar to my problem but I it did not help me: Allow to define numbers for adjacently tagged enums · Issue #2479 · serde-rs/serde · GitHub

Can you give examples of such topics?

It's possible to use untagged if you make the Message itself an enum, and use one-variant enums as markers. Check out this playground. Unfortunately, this way the timestamp is not "extracted to the top level" of the message, but perhaps that's not a big deal.

You can make a simple manual deserialize impl to rewrap to the nicer enum. Playground.

Serde untagged is kinda slow (ok, in this case there's max one token to "rewind" and only two variants to check so it won't cause slowdown I guess. Although the quality of errors with untagged are still a reason to avoid untagged :)), so an alternative could be to stash the "yet unknown" update into RawValue and manually dispatch on the first element of the tuple. Playground. (Ugh, the idea seemed simple, but the error convertion logic around RawValue turned out ugly. Anyway).

Or you could write a fully manual impl with a visitor (which I guess you wanted to avoid :)) Playground (with a bit of help of Chat GPT – didn't know about the SeqAccess and next_elem)

Thank you for your response! The first variant might be the best way, will this have any negative impact on performance when I have about 10 Topics more?

And yes the fully manual implementation is what I wanted to avoid, as I though it should be possible without it somehow.

Yes Sure!
I would have a Update that looks like this for one Topic:

{
	"44": {
		"lastLap": "10:10.100",
		"more": "fields",
		"sectors": ["better", "worse"]
	},
	"4": {
		"lastLap": "10:10.100",
		"more": "fields",
		"sectors": ["worse", "better"]
	}
}

And the update for a different topic would look like this:

{
	"44": {
		"currentTire": "SOFT",
		"other": "fields"
	},
	"4": {
		"currentTire": "SOFT",
		"other": "fields"
	}
}

As you can see both Updates have the same Hashmap with driver numbers but the content of those fields have different content. This is still a simple example the real data structures are more nested.