Skip to content

Duplicate keys in one block keep the last value, although YamlSerializer's code and comment say the first occurrence is preserved #154

Description

@matt-edmondson

What's wrong

Frontmatter/YamlSerializer.cs (~lines 74–82) says:

// Convert dictionary keys to strings and preserve the first occurrence of duplicate keys
foreach (KeyValuePair<object, object> pair in rawData)
{
    ...
    // Only add the key if it doesn't already exist
    if (!result.ContainsKey(key))

By the time this loop runs, YamlDotNet's Deserialize<Dictionary<object, object>> has already collapsed duplicates and kept the last value. The ContainsKey guard never sees a duplicate, so the "first occurrence" logic is dead code.

Repro

Frontmatter.ExtractFrontmatter("---\ntitle: First\ntitle: Second\n---\nbody\n");
// title = "Second"

Why it matters

The actual behaviour (last wins within a block) contradicts both this code's stated intent and the first-block-wins rule CombineFrontmatterObjects applies across blocks. A document that accidentally repeats a key (common after hand merges) gets the opposite value from the one the library says it keeps, and Combine then persists that choice.

Suggested fix

Pick one behaviour and make the code match it:

  • First wins (matches the comment and the cross-block rule): parse with the representation model (YamlStream / YamlMappingNode) and skip keys already seen.
  • Reject duplicates: build the deserializer with .WithDuplicateKeyChecking(), treat the block as unparseable, and fix the comment.

Acceptance criteria

  • The repro yields the documented value (First, or a parse failure if you choose rejection).
  • A test pins the behaviour.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions