File: xml.md

package info (click to toggle)
liquidsoap 2.4.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 12,372 kB
  • sloc: ml: 71,806; javascript: 27,320; ansic: 398; xml: 114; sh: 99; lisp: 96; makefile: 26
file content (160 lines) | stat: -rw-r--r-- 4,538 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
## Importing/exporting XML values

Support for XML parsing and rendering was first added in liquidsoap `2.3.1`.

You can parse XML strings using a decorator and type annotation. There are two different representations of XML you can use.

### Record access representation

This is the easiest representation. It is intended for quick access to parsed value via
record and tuples.

Here's an example:

```liquidsoap
s =
'<bla param="1" bla="true">
  <foo opt="12.3">gni</foo>
  <bar />
  <bar>bla</bar>
  <blo>1.23</blo>
  <blu>false</blu>
  <ble>123</ble>
</bla>'

let xml.parse (x :
{
  bla: {
    foo: string.{ xml_params: {opt: float} },
    bar: (unit * string),
    blo: float,
    blu: bool,
    ble: int,
    xml_params: { bla: bool }
  }
}
) = s

print("The value for ble is: #{x.bla.ble}")
```

Things to note:

- The basic mappings are: `<tag name> -> <tag content>`
- Tag content maps tag parameters to a `xml_params` method.
- When multiple tags are present, their values are collected as tuple (`bar` tag in the example)
- When a tag contains a single ground value (`string`, `bool`, `float` or `integer`), the mapping is from tag name to the corresponding value, with xml attributes attached as methods
- Tag parameters can be converted to ground values and omitted.

The parsing is driven by the type annotation and is intended to be permissive. For instance, this will work:

```liquidsoaop
s = '<bla>foo</bla>'

# Here, `foo` is omitted.
let xml.parse (x: { bla: unit }) = s

# x contains: { bla = () }

# Here, `foo` is made optional
let xml.parse (x: { bla: string? }) = s

# x contains: { bla = "foo" }
```

### Formal representation

Because XML format can result in complex values, the parser can also use a generic representation.

Here's an example:

```liquidsoap
s =
'<bla param="1" bla="true">
  <foo opt="12.3">gni</foo>
  <bar />
  <bar>bla</bar>
  <blo>1.23</blo>
  <blu>false</blu>
  <ble>123</ble>
</bla>'

let xml.parse (x :
  (
    string
    *
    {
      xml_params: [(string * string)],
      xml_children: [
        (
          string
          *
          {
            xml_params: [(string * string)],
            xml_children: [(string * {xml_text: string})]
          }
        )
      ]
    }
  )
) = s

# x contains:
(
  "bla",
  {
    xml_children=
      [
        (
          "foo",
          {
            xml_children=[("xml_text", {xml_text="gni"})],
            xml_params=[("opt", "12.3")]
          }
        ),
        ("bar", {xml_children=[], xml_params=[]}),
        (
          "bar",
          {
            xml_children=[("xml_text", {xml_text="bla"})],
            xml_params=[("option", "aab")]
          }
        ),
        (
          "blo",
          {xml_children=[("xml_text", {xml_text="1.23"})], xml_params=[]}
        ),
        (
          "blu",
          {xml_children=[("xml_text", {xml_text="false"})], xml_params=[]}
        ),
        (
          "ble",
          {xml_children=[("xml_text", {xml_text="123"})], xml_params=[]}
        )
      ],
    xml_params=[("param", "1"), ("bla", "true")]
  }
)
```

This representation is much less convenient to manipulate but allows an exact representation of all XML values.

Things to note:

- XML nodes are represented by a pair of the form: `(<tag name>, <tag properties>)`
- `<tag properties>` is a record containing the following methods:
  - `xml_params`, represented as a list of pairs `(string * string)`
  - `xml_children`, containing a list of the XML node's children. Each entry in the list is a node in the formal XML representation.
  - `xml_text`, present when the node is a text node. In this case, `xml_params` and `xm_children` are empty.
- By convention, text nodes are labelled `xml_text` and are of the form: `{ xml_text: "node content" }`

### Rendering XML values

XML values can be converted back to strings using `xml.stringify`.

Both the formal and record-access form can be rendered back into XML strings however, with the record-access representations, if a node has multiple children with the same tag, the conversion to XML string will fail.

More generally, if the values you want to convert to XML strings are complex, for instance if they use several times the same tag as child node or if the order of child nodes matters, we recommend using the formal representation to make sure that children ordering is properly preserved.

This is because record methods are not ordered in the language so we make no guarantee that the child nodes they represent be rendered in a specific order.