• Stars
    star
    163
  • Rank 231,141 (Top 5 %)
  • Language
    Elixir
  • License
    MIT License
  • Created over 10 years ago
  • Updated over 2 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Elixir library for generating XML

XML Builder

CI Module Version Hex Docs Total Download License Last Updated

Overview

An Elixir library for building XML. It is inspired by the late Jim Weirich's awesome builder library for Ruby.

Each XML node is structured as a tuple of name, attributes map, and content/list.

{name, attrs, content | list}

Installation

Add dependency to your project's mix.exs:

def deps do
  [{:xml_builder, "~> 2.1"}]
end

Examples

A simple element

Like <person id="12345">Josh</person>, would look like:

{:person, %{id: 12345}, "Josh"} |> XmlBuilder.generate

An element with child elements

Like <person id="12345"><first>Josh</first><last>Nussbaum</last></person>.

{: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.

XmlBuilder.document(:person, "Josh") |> XmlBuilder.generate

Outputs:

<?xml version="1.0" encoding="UTF-8" ?>
<person>Josh</person>

Building up an element

An element can be built using multiple calls to the element function.

import XmlBuilder

def person(id, first, last) do
  element(:person, %{id: id}, [
    element(:first, first),
    element(:last, last)
  ])
end

iex> [person(123, "Steve", "Jobs"),
      person(456, "Steve", "Wozniak")] |> generate

Outputs.

<person id="123">
  <first>Steve</first>
  <last>Jobs</last>
</person>
<person id="456">
  <first>Steve</first>
  <last>Wozniak</last>
</person>

Using keyed lists

The previous example can be simplified using a keyed list.

import XmlBuilder

def person(id, first, last) do
  element(:person, %{id: id}, first: first,
                              last: last)
end

iex> person(123, "Josh", "Nussbaum") |> generate(format: :none)
"<person id=\"123\"><first>Josh</first><last>Nussbaum</last></person>"

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.

import XmlBuilder

iex> generate({:example, [xmlns: "http://schemas.example.tld/1999"], "content"})
"<example xmlns=\"http://schemas.example.tld/1999\">content</example>"

iex> generate({:"nsName:elementName", ["xmlns:nsName": "http://schemas.example.tld/1999"], "content"})
"<nsName:elementName xmlns:nsName=\"http://schemas.example.tld/1999\">content</nsName:elementName>"

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:

import XmlBuilder

document([
  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 version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>Hello, world!</html>

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.

import XmlBuilder

document(:oldschool)
|> generate(encoding: "ISO-8859-1")
|> :unicode.characters_to_binary(:unicode, :latin1)

Outputs.

<?xml version="1.0" encoding="ISO-8859-1"?>
<oldschool/>

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:

import XmlBuilder

entries = [%{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 (&amp; etc.)
  • If content is wrapped in {:cdata, cdata}, the content in cdata is wrapped with <![CDATA[...]]>, 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.

import XmlBuilder

document(:outsider)
|> generate(standalone: true)

Outputs.

<?xml version="1.0" standalone="yes"?>
<outsider/>

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 version="1.0" standalone="no"?>
<outsider/>

Formatting

To remove indentation, pass format: :none option to XmlBuilder.generate/2.

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. Copyright (c) 2014-present, Joshua Nussbaum. All rights reserved.

More Repositories

1

svelte-persisted-store

A Svelte store that persists to localStorage
TypeScript
963
star
2

design-patterns-in-elixir

Common design patterns applied in Elixir
Elixir
598
star
3

react-hooks-in-svelte

React hook examples ported to Svelte
JavaScript
491
star
4

svelte-stripe

Everything you need to add Stripe Elements to your Svelte project
Svelte
409
star
5

commerce_billing

A payment processing library for Elixir
Elixir
176
star
6

micro-svelte-compiler

Micro Svelte compiler (naive clone)
JavaScript
113
star
7

airbadge

Svelte
87
star
8

phoenix-billing-demo

A demo of using commerce_billing with phoenix
Elixir
61
star
9

sveltekit-saas

JavaScript
59
star
10

angular-rails-api

A template for creating angular+rails apps
Ruby
53
star
11

elixir-ffi

Foreign Function Interface (FFI) for Elixir
Elixir
53
star
12

spree-faq

Frequently Asked Question extension for Spree
Ruby
46
star
13

svelte-bridge

Use your Svelte components from React & Vue.
JavaScript
42
star
14

sky-cart

Cloud-native headless shopping cart
JavaScript
36
star
15

vite-plugin-notifier

Get visual feedback that HMR completed
JavaScript
36
star
16

sveltekit-stripe-subscriptions

Example of Stripe Subscriptions & PaymentElement with SvelteKit
JavaScript
33
star
17

svelte-kit-ecommerce-3d

Demo using Threlte, SvelteKit and Stripe Checkout
Svelte
33
star
18

svelte-markdoc

Markdoc preprocessor for Svelte
JavaScript
33
star
19

supabase-active-record

ActiveRecord pattern for supabase.io
JavaScript
32
star
20

blip

A fault-tolerant and concurrent StatsD server
Elixir
30
star
21

blog-template

A markdown-style blog template for Sapper
JavaScript
28
star
22

xuber2

An example ridesharing app built with Elixir
Elixir
25
star
23

svelte-animation-store

JavaScript
24
star
24

hypersonic

A multi-player markdown editor and wiki.
Svelte
21
star
25

shiki-transformer-copy-button

A Shiki Transformer that adds a Copy button
JavaScript
20
star
26

svelte-http-store

A Svelte store backed by the fetch API
JavaScript
19
star
27

spree-html-email

HTML Formatted emails for spree
Ruby
18
star
28

spree-sitemap-generator

Spree Sitemap Generator
Ruby
17
star
29

glue

React hook for server-side state
JavaScript
17
star
30

tonys-apizza

POS for a pizza shop. Using SvelteKit, Prisma, and Stripe.
Svelte
17
star
31

sveltekit-reroute

JavaScript
17
star
32

bcm2835

Ruby bindings for libbcm2835
Ruby
17
star
33

spree-contact-form

Spree extension that provides a basic contact form
Ruby
14
star
34

vertex

Store multi-tenant metrics in ClickHouse
Elixir
14
star
35

ride_share

A clone of Uber/Lyft
Elixir
13
star
36

threlte-svg-extrusion

Svelte
12
star
37

svelte-kit-test-helpers

Testing utilities for SvelteKit
JavaScript
12
star
38

materialize-svelte

JavaScript
12
star
39

sprionic-demo

Demo Spree+Ionic Android/iOS App
JavaScript
12
star
40

threlte-starter

Starter template for SvelteKit + Threlte projects
Svelte
11
star
41

markdown-mail

Example CRUD app with SvelteKit + Prisma
JavaScript
11
star
42

1000experiments.dev

Svelte
11
star
43

auth-env

Load auth.js providers dynamically, by detecting environment variables
JavaScript
11
star
44

git-ssh-server

A small git/ssh server for hosting repos
JavaScript
10
star
45

phoenix_svelte_rollup_example

Elixir
10
star
46

svelte-codemirror

Svelte
9
star
47

combinatorics

Combinatoric utilities for Elixir
Elixir
9
star
48

airbadge-example

JavaScript
8
star
49

svelte-time-distance

Svelte component to display time distances in a human readable format.
JavaScript
8
star
50

transponder

Elixir
7
star
51

testing-with-sveltekit

JavaScript
7
star
52

spree-product-feed

A Spree extension that provides an RSS feed for products
Ruby
7
star
53

svelte-snowpack-template

A snowpack template for svelte with support for jest, eslint, postcss, tailwind, and rollup.
JavaScript
7
star
54

idp-experiment

JavaScript
6
star
55

httpx

A tiny concurrent & fault-tolerant HTTP server that uses pattern matching for routing
Elixir
6
star
56

nmea

Parser for NMEA compliant sensors
Elixir
6
star
57

svelte-preprocess-tailwind

Preprocess tailwind expressions with Svelte
JavaScript
5
star
58

spree_stock_notifications

Get notifications when stock is low or item goes out of stock
Ruby
5
star
59

rails-jquery-sortable

Example of a Rails app using jQuery Sortable plugin
Ruby
5
star
60

river

Example distributed key/value store using RAFT
Elixir
5
star
61

requel

Import SQL files with Vite
TypeScript
5
star
62

prisma-database-cleaner

Utility for cleaning Prisma databases between test runs
JavaScript
4
star
63

scrolly-snap-telling

Svelte
4
star
64

live_imu

Displays IMU data using Phoenix LiveView
Elixir
4
star
65

xuber-old

Demo of Phoenix Channels + GeoLocation API
Elixir
4
star
66

svelte-kit-pusher-example

Svelte
4
star
67

ecto_preloader

Preload your data efficiently with joins
Elixir
4
star
68

svelte-esm-loader-experimental

JavaScript
4
star
69

svelte-kit-with-docker

Run your SvelteKit dev environment with Docker
JavaScript
4
star
70

cloud-scaling-experiment

Shell
4
star
71

svelte-codemirror-test

Svelte
4
star
72

otp-talk

Elixir
4
star
73

spree-awesome-blog

Basic blog and management tools designed for Spree
JavaScript
4
star
74

guzzle

Multi-core streaming build system
Elixir
4
star
75

supabase-reactions

Svelte
4
star
76

math4devs.com

Svelte
4
star
77

svelte-chart

Svelte
3
star
78

elixir_vite_app

Elixir
3
star
79

name-panda

JavaScript
3
star
80

intel8085

Assembler, Emulator and Debugger for the Intel 8085A microprocessor
C++
3
star
81

sk-cookie-store-experiment

JavaScript
3
star
82

elixir_inventory_management_sample

Example inventory management system w/ Elixir
Elixir
3
star
83

open-props-mobile-menu

Svelte
3
star
84

unsearch

A set of adapters for working with common search engines
TypeScript
2
star
85

electron

2
star
86

rails-templates

Rails Application Templates
Ruby
2
star
87

eagle-ruby

Ruby
2
star
88

http-shark

An HTTP proxy & GUI for recording/debugging REST API calls.
CSS
2
star
89

covid-ca

CSS
2
star
90

faas

Elixir
2
star
91

gql_commerce

JavaScript
2
star
92

svelte-checkout

JavaScript
2
star
93

octobase

Hybrid relational/document database
Erlang
2
star
94

tiltd

A rack daemon for tilt
Ruby
2
star
95

http-firebase-proxy

Proxy HTTP requests, and record them in Firebase for debugging
JavaScript
2
star
96

osm-svelte-kit-experiment

Svelte
2
star
97

multi_edit

Elixir
2
star
98

sapper-data-example

JavaScript
2
star
99

use-svelte

A React hook for loading Svelte components.
JavaScript
2
star
100

wasm-ui-component-experiment

JavaScript
2
star