c++yamlyaml-cpp

yaml-cpp doesn't roundtrip with local tags


When I YAML::Load a node with a local tag, the tag type and tag contents are not preserved.

Minimal example

I am using the 0.7.0 conan package.

auto x = YAML::Node(42);

YAML::Emitter e;
e << YAML::LocalTag("x") << x;
std::string s = e.c_str();
auto y = YAML::Load(s);

std::cout << "before: " << s << std::endl;
std::cout << "tag:    " << y.Tag() << std::endl;
std::cout << "after:  " << YAML::Dump(y) << std::endl;

prints

before: !x 42
tag:    !x
after:  !<!x> 42

Expected:

before: !x 42
tag:    x
after:  !x 42

I suppose two things are going wrong here:

  1. The leading exclamation mark is added to the tag contents
  2. The tag type changes from _Tag::Type::PrimaryHandle to _Tag::Type::Verbatim

So I have two questions:

  1. Is this a bug or am I doing it wrong? I am not 100% sure I correctly understand the complicated yaml specs for tags...
  2. Is there any way to set the _Tag::Type for an existing node as a workaround?

Note: I already posted this question as a Github issue. Please excuse the redundancy.


Solution

  • Your problem is this code:

    void EmitFromEvents::EmitProps(const std::string& tag, anchor_t anchor) {
      if (!tag.empty() && tag != "?" && tag != "!")
        m_emitter << VerbatimTag(tag);
      if (anchor)
        m_emitter << Anchor(ToString(anchor));
    }
    

    The primary problem is that the default emitter always produces verbatim tags. The secondary problem is that the node doesn't remember the tag style.

    To answer the question is this a bug: No. The YAML spec does not guarantee any form of round-tripping. An implementation is allowed to emit YAML in any style it wants as long as the output's semantics are correct.

    To answer the question of whether a workaround is possible: Not with the existing emitter – as you can see, there's no settable option that modifies this behavior. You could theoretically write an own emitter that handles this to your liking; I am not entirely sure whether the API gives you enough access to internals to easily adapt the existing emitter.

    In any case, that would be too involved for a SO answer.