README.md

parsed_it

Package Version Hex Docs

A parsing and serialization library for JSON and XML in Gleam, supporting both Erlang and JavaScript targets.

Installation

sh
1gleam add parsed_it@0.1

Quick Start

JSON Parsing

gleam
1import gleam/dynamic/decode
2import parsed_it/json
3
4type User {
5 User(name: String, email: String)
6}
7
8fn user_decoder() -> decode.Decoder(User) {
9 use name <- decode.field("name", decode.string)
10 use email <- decode.field("email", decode.string)
11 decode.success(User(name:, email:))
12}
13
14pub fn example() {
15 let json_string = "{\"name\":\"Lucy\",\"email\":\"lucy@example.com\"}"
16 let result = json.parse(from: json_string, using: user_decoder())
17 // result == Ok(User("Lucy", "lucy@example.com"))
18}

XML Parsing

gleam
1import gleam/dynamic/decode
2import parsed_it/xml
3
4type Book {
5 Book(title: String, author: String)
6}
7
8fn book_decoder() -> decode.Decoder(Book) {
9 use title <- decode.field("title", decode.field("$text", decode.string))
10 use author <- decode.field("author", decode.field("$text", decode.string))
11 decode.success(Book(title:, author:))
12}
13
14pub fn example() {
15 let xml_string = "<book><title>Gleam Guide</title><author>Lucy</author></book>"
16 let result = xml.parse(from: xml_string, using: book_decoder())
17 // result == Ok(Book(title: "Gleam Guide", author: "Lucy"))
18}

Building JSON/XML

gleam
1import parsed_it/json
2import parsed_it/xml
3
4pub fn json_example() {
5 json.object([
6 #("name", json.string("Lucy")),
7 #("age", json.int(30)),
8 ])
9 |> json.to_string
10 // "{\"name\":\"Lucy\",\"age\":30}"
11}
12
13pub fn xml_example() {
14 xml.element("user", [xml.attr("id", "1")], [
15 xml.element("name", [], [xml.string("Lucy")]),
16 ])
17 |> xml.to_string
18 // "<user id=\"1\"><name>Lucy</name></user>"
19}

Module Organization

This library uses parsed_it/* as the module namespace:

  • parsed_it/json - JSON parsing and serialization
  • parsed_it/xml - XML parsing and serialization

Import modules like this:

gleam
1import parsed_it/json
2import parsed_it/xml

API Design

Type-Safe Parsing with parse

The primary API uses labeled arguments for clarity:

gleam
1json.parse(from: json_string, using: decoder)
2xml.parse(from: xml_string, using: decoder)

Dynamic Parsing with parse_dynamic

For cases where you need to inspect raw structure before decoding:

gleam
1json.parse_dynamic(from: json_string)
2xml.parse_dynamic(from: xml_string)

XML Dynamic Structure

When using parse_dynamic or writing decoders for XML, understand the structure that the parser produces:

gleam
1// XML: <book id="123"><title>Hello</title></book>
2// Becomes:
3// {
4// "$tag": "book",
5// "$attrs": { "id": "123" },
6// "title": { "$tag": "title", "$text": "Hello" }
7// }

Special keys:

  • $tag - The element's tag name (always present)
  • $attrs - Object containing attributes (present if element has attributes)
  • $text - Text content (present if element has text content)

Child Element Multiplicity

Important: Child elements with the same tag name are grouped into arrays, while unique children remain as single objects:

gleam
1// XML: <list><item>A</item><item>B</item></list>
2// Becomes: { "$tag": "list", "item": [{ "$tag": "item", "$text": "A" }, ...] }
3
4// XML: <list><item>A</item></list>
5// Becomes: { "$tag": "list", "item": { "$tag": "item", "$text": "A" } } // NOT an array!

Write decoders that handle both cases if the multiplicity can vary:

gleam
1fn items_decoder() -> decode.Decoder(List(String)) {
2 decode.one_of([
3 // Handle array case
4 decode.field("item", decode.list(decode.field("$text", decode.string))),
5 // Handle single element case
6 decode.field("item", decode.field("$text", decode.string))
7 |> decode.map(fn(s) { [s] }),
8 ])
9}

Leaf Elements and Tag Names

When a leaf element (no children, only text) is accessed as a child, you get the full element object including $tag. The text is in $text:

gleam
1// To decode: <name>Lucy</name>
2decode.field("name", decode.field("$text", decode.string))

Known Issues

IssuePlatformImpact
Unicode corruption in XMLErlangMulti-byte UTF-8 may be corrupted
Error message extractionJavaScriptMay return empty error details in some engines

Error Handling

JSON Errors

gleam
1pub type JsonDecodeError {
2 UnexpectedEnd // JSON truncated
3 UnexpectedChar(String) // Invalid character (hex code)
4 UnexpectedSequence(String)
5 UnableToDecode(List(DecodeError)) // Valid JSON, wrong shape
6}

XML Errors

gleam
1pub type XmlDecodeError {
2 InvalidXml(String) // Malformed XML
3 UnableToDecode(List(DecodeError)) // Valid XML, wrong shape
4}

Note: Error types are currently public ADTs. This means adding new error variants in future versions would be a breaking change. We may make these opaque in a future major version to allow for better error handling evolution.

Development

sh
1gleam test # Run the tests
2gleam docs build # Build documentation

Further Documentation

API documentation is available at https://hexdocs.pm/parsed_it.