Convert Elixir maps or structs to YAML

I am building an API that needs to allow the user to download a yaml config file for an external application.
I’m not sure if I’m blind, or there is no Elixir library that converts a map (or struct) into YAML? All I can find is how to decode YAML and parse it into an Elixir data structure, but not the other way around.

1 Like

and there is a good reason for that: YAML is not a standardized format. In fact each and every implementation of YAML out there varies slightly. So when you generate YAML config file from most popular PHP library, it may or may not work as expected in most popular Ruby library etc.

I think @josevalim had a strong opinion on not using YAML in Elixir, and this sentiment has been picked up by the community, since the not any sort of standard argument is really good one. As a result we have a few parsers, since reading some legacy config files may be necessary - and no generators that I can see.

I’d probably generate some intermediate data format from my data (JSON?) and convert it to YAML with some external to Elixir tool (Ruby?).

4 Likes

By the book json is a subset of yaml, so if your clients yank parser adheres to the yaml standard, it will work…

1 Like

You see, CSV happens to be implemented in the wildest ways, yet if customers / stakeholders of my project require a CSV export, they won’t be happy with an answer like yours.

I can understand that José does not want to use YAML FOR Elixir (e.g. he said you should not use it for Elixir configuration, and I totally agree with that). My use case is a different one. I’m building an API that needs to return a YAML file, because it is needed as a config for a separate application that I cannot change.

This, in my books, would qualify as an ugly workaround. Now I can’t say in the docs: “just curl https://foo/bar.yml > bar.yml” and you’re done! Now I’d need to deliver a script or tool to make the conversion and have the user possibly have the required runtime (or use Go, but then you have to publish a binary for every major desktop OS).

All this discussion about YAML being terrible is nice and entertaining, until you hit the real world. I’m not yet convinced of why not having a YAML generator in Elixir might be a good idea.

3 Likes

Yep, this is about the only thing that all YAML parsers should agree on, that they can take normal JSON too, after that they diverge all crazy, see:

Among many many others.

Really though, the YAML ‘spec’ is horrible beyond horrible, do not use it, do not look at it, ignore that utter crap.
JSON is not bad, bit wordy, but not bad.
I quite like TOML and HOSON both (for different purposes), but JSON is basically the standard.

I thought CSV was actually quite well defined, proper cell encapsulation and escapaing and all?
But he is right, one YAML file that works in one YAML importer may not work in another (see the yaml-sucks github link above, it tests those).

Just export JSON then, YAML can read JSON no problem. JSON is a subset of YAML so it not like it is some random addon to it, valid JSON is valid YAML.

Good luck with that, almost every YAML library gets it wrong so which do you want to base it on? Or do you want to write it from scratch in Elixir/Erlang itself, and if so dang good luck there, the YAML spec is utterly monstrous and you are near guaranteed to get a lot of it wrong because it has a lot of undefined behaviour…

OK. I was trying to give you the bigger picture on why you can’t find a library you need. I guess that’s not welcome. Good luck, mate! But most likely you need to write your own. Do publish it, there are other people who can benefit.

1 Like

On the contrary! I’m very happy to get your feedback, and I always try to keep an open mind. And you are right: there is a reason YAML is considered bad for the lack of a proper specification. I’m aware of that. I was just trying to point out that, sometimes, you just need to implement a specific solution, you like it or not.

My takeaway from this discussion is that I should try using JSON as the response format and post-process it if there really is no other way around generating YAML. Thanks for the feedback!

1 Like

Valid JSON is valid YAML. Any conforming YAML parser should be able to parse JSON. So if you don’t have any specific needs for the YAML format, doing something like:

"---\n" <> Poison.encode!(data)

will produce something any YAML parser will be able to process.

8 Likes

Sorry to continue here. @matiso what you ended up with, or now 2019 have you found a solution? I have a very strict requirement that is editing yaml and put it back (cry)

Would love to hear from you all.

3 Likes

@50kudos For what it’s worth, I found myself in a similar situation. I could parse YAML with yaml-elixir but didn’t have a way to write. So I wrote a short YAML writer myself.

def to_yaml(yaml_map, indentation \\ "") do
    yaml_map
    |> Map.keys
    |> Enum.map(
      fn key ->
        value = Map.fetch!(yaml_map, key)
        cond do
          is_bitstring(value) -> "#{indentation}#{key}: #{value}"
          is_number(value) -> "#{indentation}#{key}: #{value}"
          is_map(value) -> "#{indentation}#{key}:\n#{to_yaml(value, "#{indentation}  ")}"
        end
      end
    )
    |> Enum.join("\n")
end

You use it as follows:

YamlElixir.read_from_file!("file.yaml")
|> Map.put(:some_key, %{some_nested_key: 123})
|> to_yaml

It’s not perfect but it suits my purposes (reading and writing the app.yaml configuration file in Google App Engine).

Thought I’d share to help others who come here while searching, like I did!

6 Likes
6 Likes

It actually is standardized, but implementors have taken liberties with it, and some have been reluctant to updated from 1.1 to 1.2. Yes, YAML is complex compared to JSON, but it was designed to cover more use cases. The alternative might have led to multiple ad-hoc solutions sprouting up for those additional features.