Deserialization of enum variant which recursively refers to itself failed with stackoverflow

Open
#819 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

  • #982 by @scadastrangelove — closed without merging
  • #995 by @dralley — closed without merging

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
35/100
Issue type
Bug
Clarity
Mostly clear
Activity status
Stale
Tech stack
rust
Domain
backend

Research direction

The report includes a Rust reproduction using from_str::<MathNode> and recursive enum variants. Start by reading the XML deserialization path for enum newtype variants and compare it with the struct variant behavior shown in the issue. Done means the newtype form deserializes the sample without overflowing the stack; the issue names no test file or implementation entry point.

Written by the indexing model from the issue text.

Description

bug help wanted serde

Found in https://stackoverflow.com/questions/67932584/deserializing-recursive-xml-using-serde-in-rust

Currently recursively defined enums using newtype variants for recursion lead to stack overflow error:

#[test]
fn recursive() {
    #[derive(Debug, Deserialize, PartialEq)]
    #[serde(rename_all = "camelCase")]
    enum MathNode {
        Apply(Vec<MathNode>),
        Ci(Vec<MathNode>),
        #[serde(rename = "$text")]
        Text(String),
        #[serde(rename = "math")]
        Root(Vec<MathNode>),
    }

    let test = r#"
    <math>
        <apply>
            <ci type="integer">5</ci>
        </apply>
    </math>"#;
    assert_eq!(
        from_str::<MathNode>(test).unwrap(),
        MathNode::Root(vec![MathNode::Apply(vec![MathNode::Ci(vec![
            MathNode::Text("5".into())
        ])])])
    );
}

However, if change newtype variant to the struct variant with one $value field, everything works:

#[test]
fn recursive() {
    #[derive(Debug, Deserialize, PartialEq)]
    #[serde(rename_all = "camelCase")]
    enum MathNode {
        Apply {
            #[serde(rename = "$value")]
            value: Vec<MathNode>,
        },
        Ci {
            #[serde(rename = "$value")]
            value: Vec<MathNode>,
        },
        #[serde(rename = "$text")]
        Text(String),
        #[serde(rename = "math")]
        Root {
            #[serde(rename = "$value")]
            value: Vec<MathNode>,
        },
    }

    let test = r#"
    <math>
        <apply>
            <ci type="integer">5</ci>
        </apply>
    </math>"#;
    assert_eq!(
        from_str::<MathNode>(test).unwrap(),
        MathNode::Root {
            value: vec![MathNode::Apply {
                value: vec![MathNode::Ci {
                    value: vec![MathNode::Text("5".into())]
                }],
            }],
        }
    );
}

It seems worth to implement newtype variant deserialization like such structs

Dominant language
Rust
Stars
1.6k
Forks
299
Avg merge
2d 4h
Merged PRs (30d)
9

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from tafia/quick-xml

All issues in tafia/quick-xml

Similar issues

More Rust issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.