Skip to content

Yaml dumper and parser TaggedValue should be extensible #40743

Closed
@goodevilgenius

Description

@goodevilgenius

Description

Currently, the yaml parser allows custom tags, such as these:

!bar some-text

When Yaml::parse is called on this, with the Yaml::PARSE_CUSTOM_TAGS flag, it will return a Symfony\Component\Yaml\Tag\TaggedValue that has a tag of "bar", and a value of "some-text".

I believe it would be useful if custom classes could be used, by creating a factory class that an application can hook into to add custom classes that implement a TaggedValueInterface. And the factory should be used to create new instances when parsing. If there's no available class to use for a given tag, TaggedValue would still be used.

Additionally, when dumping, if a value implements TaggedValueInterface, it would dump it as a custom tag, rather than a dictionary.

Implementation

I would be happy to write the implementation, and submit a PR. I wanted to see if there was interest in such a feature before I went to the work.

Additionally, I was thinking that an additional flag might be necessary to enable this behavior in the parser to preserve backwards compatibility.

Example

# In application bootstrap

use App\Models\User; // Implements TaggedValueInterface
use Symfony\Component\Yaml\Tag\TagFactory;

TagFactory::addTaggedClass(User::class);
// This will call a static `getYamlTags` method, which should
// return an array of tags that this class can be used, and
// add it to an array of classes on the factory.

# Alternate bootstrap

use App\Models\User; // Implements TaggedValueInterface
use Symfony\Component\Yaml\Yaml;

Yaml::addCustomTag(User::class);

# To dump yaml

use App\Models\User;
use Symfony\Component\Yaml\Yaml;

$user = User::find($id); // Will fetch a user from the database
return Yaml::dump(['user' => $user]);

// Will return something like 'user: !user <id>',
// depending on the implementation in the User class

# To parse this yaml

use App\Models\User;
use Symfony\Component\Yaml\Yaml;
use Symfony\Component\Yaml\Tag\TaggedValue;

$yaml = "user: !user 5\ntagged: !other-tag text";

// Yaml::PARSE_CUSTOM_TAG_OBJECTS would be a new flag necessary to enable the behavior and preserve backwards compatibility
$userData = Yaml::parse($yaml, Yaml::PARSE_CUSTOM_TAGS | Yaml::PARSE_CUSTOM_TAG_OBJECTS);

// Since !user is a known tag, a static method will be called
// on the User class, passing the tag and value in, same as
// the TaggedValue constructor, and this method will handle
// fetching from the database and returning an instance of User

/** @var User $user */
$user = $userData['user'];

/** @var TaggedValue */
$tagged = $userData['tagged']

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions