mirror of
https://github.com/valitydev/yamerl.git
synced 2024-11-06 02:45:23 +00:00
357 lines
8.8 KiB
Markdown
357 lines
8.8 KiB
Markdown
# yamerl: YAML 1.2 and JSON parser in Erlang
|
|
|
|
[![Build Status](https://travis-ci.org/yakaz/yamerl.svg?branch=master)](https://travis-ci.org/yakaz/yamerl)
|
|
[![Coverage Status](https://coveralls.io/repos/github/yakaz/yamerl/badge.svg?branch=master)](https://coveralls.io/github/yakaz/yamerl)
|
|
[![Hex version](https://img.shields.io/hexpm/v/yamerl.svg "Hex version")](https://hex.pm/packages/yamerl)
|
|
|
|
YAML is a human-friendly data serialization format. The specification
|
|
for this language and many examples are available from the [Official
|
|
YAML web site](http://www.yaml.org/). You may also want to check the
|
|
[YAML Wikipedia article](http://en.wikipedia.org/wiki/YAML).
|
|
|
|
**yamerl** is a pure [Erlang application](http://www.erlang.org/)
|
|
which is able to parse [YAML 1.1](http://yaml.org/spec/1.1/) and
|
|
[YAML 1.2](http://www.yaml.org/spec/1.2/spec.html) documents, as well
|
|
as [JSON](http://json.org/) documents. It only depends on standard
|
|
Erlang/OTP applications; no external dependency is required. It doesn't
|
|
use native code either (neither port drivers nor NIFs).
|
|
|
|
yamerl can be used inside Elixir projects, like any other Erlang
|
|
library. You can find an example later in this README.
|
|
|
|
yamerl is distributed under the terms of the **2-clause BSD license**;
|
|
see `LICENSE`.
|
|
|
|
## Integrate to your project
|
|
|
|
yamerl uses [Rebar 3](http://www.rebar3.org/) as its build system so
|
|
it can be integrated to many common build systems.
|
|
|
|
### Rebar
|
|
|
|
yamerl is available as a [Hex.pm package](https://hex.pm/packages/yamerl).
|
|
Thus you can simply list it as a package dependency in your `rebar.config`:
|
|
|
|
```erlang
|
|
{deps, [yamerl]}.
|
|
```
|
|
|
|
### Erlang.mk
|
|
|
|
Erlang.mk knows about yamerl. You just need to add `yamerl` as a
|
|
dependency in your `Makefile`:
|
|
|
|
```make
|
|
DEPS = yamerl
|
|
```
|
|
|
|
### Mix
|
|
|
|
You can use yamerl in your Elixir project. yamerl is available as a
|
|
[Hex.pm package](https://hex.pm/packages/yamerl). Thus you can simply
|
|
list its name in your `mix.exs`:
|
|
|
|
```elixir
|
|
def project do
|
|
[
|
|
deps: [{:yamerl, "~> 0.4.0"}]
|
|
]
|
|
end
|
|
```
|
|
|
|
## Getting started
|
|
|
|
Before using yamerl, the application must be started:
|
|
```erlang
|
|
application:start(yamerl).
|
|
```
|
|
|
|
Now, one can use the `yamerl_constr` module to parse and construct a
|
|
list of documents from:
|
|
* an in-memory document (string or binary);
|
|
* a regualr file;
|
|
* a stream.
|
|
|
|
Because a YAML input stream may contain multiple documents,
|
|
`yamerl_constr` always returns a list of documents, even if the input
|
|
stream only contains one.
|
|
|
|
### Parsing an in-memory document
|
|
|
|
```erlang
|
|
yamerl_constr:string("Hello World!").
|
|
```
|
|
```erlang
|
|
% List of documents; here, only one document.
|
|
[
|
|
% Document #1; contains a single scalar.
|
|
"Hello World!"
|
|
]
|
|
```
|
|
|
|
Here, the returned value is a list of documents containing one document.
|
|
This document has a scalar as its sole node.
|
|
|
|
### Parsing a file
|
|
|
|
Considering the following YAML file:
|
|
```yaml
|
|
# applications.yaml
|
|
- application: kernel
|
|
version: 2.15.3
|
|
path: /usr/local/lib/erlang/lib/kernel-2.15.3
|
|
- application: stdlib
|
|
version: 1.18.3
|
|
path: /usr/local/lib/erlang/lib/stdlib-1.18.3
|
|
- application: sasl
|
|
version: 2.2.1
|
|
path: /usr/local/lib/erlang/lib/sasl-2.2.1
|
|
```
|
|
|
|
```erlang
|
|
yamerl_constr:file("applications.yaml").
|
|
```
|
|
```erlang
|
|
% List of documents; again, only one document here.
|
|
[
|
|
% List of mappings.
|
|
[
|
|
% Mapping, represented as a proplist: each entry has the form {Key, Value}.
|
|
[
|
|
{"application", "kernel"},
|
|
{"version", "2.15.3"},
|
|
{"path", "/usr/local/lib/erlang/lib/kernel-2.15.3"}
|
|
], [
|
|
{"application", "stdlib"},
|
|
{"version", "1.18.3"},
|
|
{"path", "/usr/local/lib/erlang/lib/stdlib-1.18.3"}
|
|
], [
|
|
{"application", "sasl"},
|
|
{"version", "2.2.1"},
|
|
{"path", "/usr/local/lib/erlang/lib/sasl-2.2.1"}
|
|
]
|
|
]
|
|
]
|
|
```
|
|
|
|
### Parsing a stream
|
|
|
|
The developer is responsible for reading the stream and provide the
|
|
chunks to yamerl.
|
|
|
|
```erlang
|
|
% Initialize a new construction state. It takes a term describing the
|
|
% source; it may be any Erlang term.
|
|
Parser0 = yamerl_constr:new({file, "<stdin>"}),
|
|
|
|
% Read chunks and feed the parser. A new parser state is returned.
|
|
{continue, Parser1} = yamerl_constr:next_chunk(Parser0, Chunk1),
|
|
% ...
|
|
{continue, Parser2} = yamerl_constr:next_chunk(Parser1, Chunk2),
|
|
|
|
% When the stream ends, tell the parser it's the last chunk.
|
|
Documents = yamerl_constr:last_chunk(Parser2, Chunk3).
|
|
```
|
|
|
|
## Simple vs. full document structures
|
|
|
|
`yamerl_constr` comes with two built-in modes:
|
|
* It can output simple documents, eg. documents based on basic Erlang
|
|
structures (strings, numbers, lists, proplists). This is the default
|
|
mode.
|
|
* It can output detailed documents using records. These records carry
|
|
more information such as line/column, tag URI, YAML node type, module
|
|
used to construct it, etc.
|
|
|
|
If we use the following YAML document:
|
|
```yaml
|
|
# system.yaml
|
|
- os: FreeBSD
|
|
version: 9.0-RELEASE-p3
|
|
```
|
|
|
|
* Simple documents:
|
|
|
|
```erlang
|
|
yamerl_constr:file("system.yaml").
|
|
```
|
|
|
|
```erlang
|
|
% List of documents.
|
|
[
|
|
% List of mappings.
|
|
[
|
|
% Mapping with two entries.
|
|
[
|
|
{"os", "FreeBSD"},
|
|
{"version","9.0-RELEASE-p3"}
|
|
]
|
|
]
|
|
]
|
|
```
|
|
|
|
* Full documents:
|
|
|
|
```erlang
|
|
yamerl_constr:file("system.yaml", [{detailed_constr, true}]).
|
|
```
|
|
|
|
```erlang
|
|
% List of documents.
|
|
[
|
|
% Document with a list as its root node.
|
|
{yamerl_doc,
|
|
{yamerl_seq, yamerl_node_seq, "tag:yaml.org,2002:seq", [{line, 2}, {column, 1}], [
|
|
% Mapping #1.
|
|
{yamerl_map, yamerl_node_map, "tag:yaml.org,2002:map", [{line, 2}, {column, 3}], [
|
|
{
|
|
% Mapping entry #1.
|
|
{yamerl_str, yamerl_node_str, "tag:yaml.org,2002:str", [{line, 2}, {column, 3}], "os"},
|
|
{yamerl_str, yamerl_node_str, "tag:yaml.org,2002:str", [{line, 2}, {column, 7}], "FreeBSD"}
|
|
}, {
|
|
% Mapping entry #2.
|
|
{yamerl_str, yamerl_node_str, "tag:yaml.org,2002:str", [{line, 3}, {column, 3}], "version"},
|
|
{yamerl_str, yamerl_node_str, "tag:yaml.org,2002:str", [{line, 3}, {column, 12}], "9.0-RELEASE-p3"}
|
|
}
|
|
]}
|
|
],
|
|
1}
|
|
}
|
|
]
|
|
```
|
|
|
|
## Use yamerl in an Elixir project
|
|
|
|
Here is a complete example:
|
|
|
|
1. You first need to add `yamerl` to the dependencies list in `mix.exs`:
|
|
|
|
```elixir
|
|
# mix.exs, created with `mix new myapp` and updated to have `yamerl` as
|
|
# a dependency.
|
|
defmodule Myapp.Mixfile do
|
|
use Mix.Project
|
|
|
|
def project do
|
|
[app: :myapp,
|
|
version: "0.1.0",
|
|
elixir: "~> 1.3",
|
|
build_embedded: Mix.env == :prod,
|
|
start_permanent: Mix.env == :prod,
|
|
deps: deps()]
|
|
end
|
|
|
|
# Configuration for the OTP application
|
|
#
|
|
# Type "mix help compile.app" for more information
|
|
def application do
|
|
[applications: [:logger]]
|
|
end
|
|
|
|
# Dependencies can be Hex packages:
|
|
#
|
|
# {:mydep, "~> 0.3.0"}
|
|
#
|
|
# Or git/path repositories:
|
|
#
|
|
# {:mydep, git: "https://github.com/elixir-lang/mydep.git", tag: "0.1.0"}
|
|
#
|
|
# Type "mix help deps" for more examples and options
|
|
defp deps do
|
|
[
|
|
{:yamerl, "~> 0.4.0"}
|
|
]
|
|
end
|
|
end
|
|
```
|
|
|
|
2. Start the `yamerl` application and use the constructor, either in
|
|
simple or detailed mode:
|
|
|
|
```elixir
|
|
# lib/myapp.ex
|
|
defmodule Myapp do
|
|
def simple(filename) do
|
|
# The yamerl application must be started before any use of it.
|
|
Application.start(:yamerl)
|
|
|
|
:yamerl_constr.file(filename)
|
|
end
|
|
|
|
def detailed(filename) do
|
|
# The yamerl application must be started before any use of it.
|
|
Application.start(:yamerl)
|
|
|
|
:yamerl_constr.file(filename, [:detailed_constr])
|
|
end
|
|
end
|
|
```
|
|
|
|
Now let's use the `Myapp` module to parse the same YAML example file as
|
|
above:
|
|
|
|
```yaml
|
|
# system.yaml
|
|
- os: FreeBSD
|
|
version: 9.0-RELEASE-p3
|
|
```
|
|
|
|
* Parsing in simple mode:
|
|
|
|
```elixir
|
|
Myapp.simple("system.yaml")
|
|
```
|
|
|
|
```elixir
|
|
# List of documents.
|
|
[
|
|
# List of mappings.
|
|
[
|
|
# Mapping with two entries.
|
|
[
|
|
{'os', 'FreeBSD'},
|
|
{'version', '9.0-RELEASE-p3'}
|
|
]
|
|
]
|
|
]
|
|
```
|
|
|
|
* Parsing in detailed mode:
|
|
|
|
```elixir
|
|
Myapp.detailed("system.yaml")
|
|
```
|
|
|
|
```elixir
|
|
# List of documents.
|
|
[
|
|
# Document with a list as its root node.
|
|
yamerl_doc:
|
|
{:yamerl_seq, :yamerl_node_seq, 'tag:yaml.org,2002:seq', [line: 2, column: 1],
|
|
[
|
|
# Mapping #1.
|
|
{:yamerl_map, :yamerl_node_map, 'tag:yaml.org,2002:map', [line: 2, column: 3],
|
|
[
|
|
# Mapping entry #1.
|
|
{
|
|
{:yamerl_str, :yamerl_node_str, 'tag:yaml.org,2002:str', [line: 2, column: 3], 'os'},
|
|
{:yamerl_str, :yamerl_node_str, 'tag:yaml.org,2002:str', [line: 2, column: 7], 'FreeBSD'}
|
|
},
|
|
# Mapping entry #2.
|
|
{
|
|
{:yamerl_str, :yamerl_node_str, 'tag:yaml.org,2002:str', [line: 3, column: 3], 'version'},
|
|
{:yamerl_str, :yamerl_node_str, 'tag:yaml.org,2002:str', [line: 3, column: 12], '9.0-RELEASE-p3'}
|
|
}
|
|
]
|
|
}
|
|
],
|
|
1
|
|
}
|
|
]
|
|
```
|
|
|
|
## Complete documentation
|
|
|
|
See https://hexdocs.pm/yamerl/ for a complete user guide and reference
|
|
manual.
|