New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#[serde(flatten)] attribute support for enum variants #1402

Open
srijs opened this Issue Oct 2, 2018 · 2 comments

Comments

2 participants
@srijs

srijs commented Oct 2, 2018

Hi!

I have the following use-case for a feature similar to #[serde(flatten)], but for enum variants rather than struct fields. Generally happy to dive into serde_derive myself in case you think this would be worth having, but it could also be that I'm trying to model this the wrong way.

I'm trying to parse JSON values in the estree format. The format specifies an ECMAScript syntax tree using an inhertance relationship, where generally only the nodes at the bottom of the hierarchy are tagged.

For example, one type of node is Expression, but there doesn't exist any node with the "Expression" tag, because the tag is assigned to the "concrete" expression nodes such as Identifier or Literal. The representation of the Expression node is basically just a union of all possible JSON representations of its descendant node types.

So far, I've been modelling it like this, which has been working fine for the most part:

#[derive(Deserialize)]
#[serde(tag = "type")]
pub enum Expression {
    Identifier { name: String },
    Literal { value: Value },
    ...
}

Now, the difficulty I've been having comes in when I try to mix types that are at the bottom of the node hierarchy with node types that have sub-types (such as Expression):

#[derive(Deserialize)]
#[serde(tag = "type")]
pub enum ForStatementInit {
    VariableDeclaration { kind: String, ... },
    Expression(Expression),
}

In this case, what I really want is for the ForStatementInit type to be the result of "adding" the node of type VariableDeclaration to the set of representations for Expression. What ends up happening though of course is that it expects a value tagged with either VariableDeclaration or Expression.

I can work around this by either nesting VariableDeclaration into a single-variant enum, and using #[serde(untagged)] on the ForStatementInit enum, or by manually "flattening" the expression types, like so:

#[derive(Deserialize)]
#[serde(tag = "type")]
pub enum ForStatementInit {
    VariableDeclaration { kind: String, ... },
    // Expression
    Identifier { name: String },
    Literal { value: Value },
}

However, what I think would be really useful is to be able to do something like this instead, which would apply the same "flattening" transformation in the example above:

#[derive(Deserialize)]
#[serde(tag = "type")]
pub enum ForStatementInit {
    VariableDeclaration { kind: String, ... },
    #[serde(flatten)]
    Expression(Expression),
}

Hope this makes somewhat sense, keen to hear if this is something that you think is worth solving in serde itself!

@srijs

This comment has been minimized.

Show comment
Hide comment
@srijs

srijs Oct 2, 2018

It looks like this is similar to #1350 where you suggest manually flattening, but that would be fairly cumbersome in this case (Expression might have a couple dozen variants) and I guess I'm still curious whether there's a "better" way to do this.

srijs commented Oct 2, 2018

It looks like this is similar to #1350 where you suggest manually flattening, but that would be fairly cumbersome in this case (Expression might have a couple dozen variants) and I guess I'm still curious whether there's a "better" way to do this.

@dtolnay

This comment has been minimized.

Show comment
Hide comment
@dtolnay

dtolnay Oct 2, 2018

Member

Seems reasonable! I would be interested in supporting this better. I have also occasionally wanted serde(untagged) to work as a variant attribute which would have a similar effect in this case.

#[derive(Deserialize)]
#[serde(tag = "type")]
pub enum ForStatementInit {
    VariableDeclaration { kind: String, ... },
    #[serde(untagged)]
    Expression(Expression),
}
Member

dtolnay commented Oct 2, 2018

Seems reasonable! I would be interested in supporting this better. I have also occasionally wanted serde(untagged) to work as a variant attribute which would have a similar effect in this case.

#[derive(Deserialize)]
#[serde(tag = "type")]
pub enum ForStatementInit {
    VariableDeclaration { kind: String, ... },
    #[serde(untagged)]
    Expression(Expression),
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment