Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/joshnuss/xml_builder
Elixir library for generating XML
https://github.com/joshnuss/xml_builder
elixir xml
Last synced: 9 days ago
JSON representation
Elixir library for generating XML
- Host: GitHub
- URL: https://github.com/joshnuss/xml_builder
- Owner: joshnuss
- License: mit
- Created: 2014-07-16T16:02:14.000Z (over 10 years ago)
- Default Branch: master
- Last Pushed: 2024-04-23T04:28:28.000Z (7 months ago)
- Last Synced: 2024-10-18T16:49:18.015Z (21 days ago)
- Topics: elixir, xml
- Language: Elixir
- Homepage:
- Size: 113 KB
- Stars: 174
- Watchers: 5
- Forks: 38
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
- freaking_awesome_elixir - Elixir - Elixir library for generating xml. (XML)
- fucking-awesome-elixir - xml_builder - Elixir library for generating xml. (XML)
- awesome-elixir - xml_builder - Elixir library for generating xml. (XML)
README
XML Builder
===========[![CI](https://github.com/joshnuss/xml_builder/workflows/mix/badge.svg)](https://github.com/joshnuss/xml_builder/actions)
[![Module Version](https://img.shields.io/hexpm/v/xml_builder.svg)](https://hex.pm/packages/xml_builder)
[![Hex Docs](https://img.shields.io/badge/hex-docs-lightgreen.svg)](https://hexdocs.pm/xml_builder/)
[![Total Download](https://img.shields.io/hexpm/dt/xml_builder.svg)](https://hex.pm/packages/xml_builder)
[![License](https://img.shields.io/hexpm/l/xml_builder.svg)](https://github.com/joshnuss/xml_builder/blob/master/LICENSE)
[![Last Updated](https://img.shields.io/github/last-commit/joshnuss/xml_builder.svg)](https://github.com/joshnuss/xml_builder/commits/master)## Overview
An Elixir library for building XML. It is inspired by the late [Jim Weirich](https://github.com/jimweirich)'s awesome [builder](https://github.com/jimweirich/builder) library for Ruby.
Each XML node is structured as a tuple of name, attributes map, and content/list.
```elixir
{name, attrs, content | list}
```## Installation
Add dependency to your project's `mix.exs`:
```elixir
def deps do
[{:xml_builder, "~> 2.1"}]
end
```## Examples
### A simple element
Like `Josh`, would look like:
```elixir
{:person, %{id: 12345}, "Josh"} |> XmlBuilder.generate
```### An element with child elements
Like `JoshNussbaum`.
```elixir
{:person, %{id: 12345}, [{:first, nil, "Josh"}, {:last, nil, "Nussbaum"}]} |> XmlBuilder.generate
```### Convenience Functions
For more readability, you can use XmlBuilder's methods instead of creating tuples manually.
```elixir
XmlBuilder.document(:person, "Josh") |> XmlBuilder.generate
```Outputs:
```xml
Josh
```#### Building up an element
An element can be built using multiple calls to the `element` function.
```elixir
import XmlBuilderdef person(id, first, last) do
element(:person, %{id: id}, [
element(:first, first),
element(:last, last)
])
endiex> [person(123, "Steve", "Jobs"),
person(456, "Steve", "Wozniak")] |> generate
```Outputs.
```xml
Steve
JobsSteve
Wozniak```
#### Using keyed lists
The previous example can be simplified using a keyed list.
```elixir
import XmlBuilderdef person(id, first, last) do
element(:person, %{id: id}, first: first,
last: last)
endiex> person(123, "Josh", "Nussbaum") |> generate(format: :none)
"JoshNussbaum"
```#### Namespaces
To use a namespace, add an `xmlns` attribute to the root element.
To use multiple schemas, specify a `xmlns:nsName` attribute for each schema and use a colon `:` in the element name, ie `nsName:elementName`.
```elixir
import XmlBuilderiex> generate({:example, [xmlns: "http://schemas.example.tld/1999"], "content"})
"content"iex> generate({:"nsName:elementName", ["xmlns:nsName": "http://schemas.example.tld/1999"], "content"})
"content"
```### DOCTYPE declarations
A DOCTYPE can be declared by applying the `doctype` function at the first position of a list of elements in a `document` definition:
```elixir
import XmlBuilderdocument([
doctype("html", public: ["-//W3C//DTD XHTML 1.0 Transitional//EN",
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"]),
element(:html, "Hello, world!")
]) |> generate
```Outputs.
```xml
Hello, world!
```### Encoding
While the output is always UTF-8 and has to be converted in another place, you can override the encoding statement in the XML declaration with the `encoding` option.
```elixir
import XmlBuilderdocument(:oldschool)
|> generate(encoding: "ISO-8859-1")
|> :unicode.characters_to_binary(:unicode, :latin1)
```Outputs.
```xml
```
### Using `iodata()` directly
While by default, output from `generate/2` is converted to `binary()`, you can use `generate_iodata/2` to skip this conversion. This can be convenient if you're using `IO.binwrite/2` on a `:raw` IO device, as these APIs can work with `iodata()` directly, leading to some performance gains.
In some scenarios, it may be beneficial to generate part of your XML upfront, for instance when generating a `sitemap.xml`, you may have shared fields for `author`. Instead of generating this each time, you could do the following:
```elixir
import XmlBuilderentries = [%{title: "Test", url: "https://example.org/"}]
# Generate static author data upfront
author = generate_iodata(element(:author, [
element(:name, "John Doe"),
element(:uri, "https://example.org/")
]))file = File.open!("path/to/file", [:raw])
for entry <- entries do
iodata =
generate_iodata(element(:entry, [
# Reuse the static pre-generated fields as-is
{:iodata, author},# Dynamic elements are generated for each entry
element(:title, entry.title),
element(:link, entry.url)
]))IO.binwrite(file, iodata)
end
```### Escaping
XmlBuilder offers 3 distinct ways to control how content of tags is escaped and handled:
- By default, any content is escaped, replacing reserved characters (`& " ' < >`) with their equivalent entity (`&` etc.)
- If content is wrapped in `{:cdata, cdata}`, the content in `cdata` is wrapped with ``, and not escaped. You should make sure the content itself does not contain `]]>`.
- If content is wrapped in `{:safe, data}`, the content in `data` is not escaped, but will be stringified if not a bitstring. Use this option carefully. It may be useful when data is guaranteed to be safe (numeric data).
- If content is wrapped in `{:iodata, data}`, either in the top level or within a list, the `data` is used as `iodata()`, and will not be escaped, indented or stringified. An example of this can be seen in the "Using `iodata()` directly" example above.### Standalone
Should you need `standalone="yes"` in the XML declaration, you can pass `standalone: true` as option to the `generate/2` call.
```elixir
import XmlBuilderdocument(:outsider)
|> generate(standalone: true)
```Outputs.
```xml
```
If otherwise you need `standalone ="no"` in the XML declaration, you can pass `standalone: false` as an option to the` generate / 2` call.
Outputs.
```xml
```
### Formatting
To remove indentation, pass `format: :none` option to `XmlBuilder.generate/2`.
```elixir
doc |> XmlBuilder.generate(format: :none)
```The default is to formatting with indentation, which is equivalent to `XmlBuilder.generate(doc, format: :indent)`.
## License
This source code is licensed under the [MIT License](https://github.com/joshnuss/xml_builder/blob/master/LICENSE). Copyright (c) 2014-present, Joshua Nussbaum. All rights reserved.