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 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334
|
---
layout: default
title: Output Formats
---
<div class="hgroup">
<div class="hgroup-inline">
<div class="panel">
<h1 id="top">Output Formats</h1>
</div>
<div style="clear: both"></div>
</div>
</div>
<div class="hgroup">
<div class="hgroup-inline">
<div class="panel">
<p>
A Jsonnet evaluation typically outputs a JSON document. For ways to output multiple JSON
documents, see <a href="/learning/getting_started.html">Getting Started</a>. Whether this
integrates cleanly depends on your application. If you're writing a custom application,
then you can simply consume this JSON. Another option is to write your config as a protocol
buffer and deserialize Jsonnet's JSON output using the proto3 canonical JSON representation.
Interoperation with YAML is also easy, see <a
href="/articles/kubernetes.html">Kubernetes</a> for an example. To support consumers of
syntactically incompatible formats such as INI or XML, custom serialization is needed.
</p>
</div>
<div style="clear: both"></div>
</div>
</div>
<div class="hgroup">
<div class="hgroup-inline">
<div class="panel">
<h2 id="top">Custom Output Formats</h2>
</div>
<div style="clear: both"></div>
</div>
</div>
<div class="hgroup">
<div class="hgroup-inline">
<div class="panel">
<p>
The INI format provides a simple example.
</p>
<ul>
<li>
The content of the INI file has been represented within the JSON object model, i.e. using
objects to represent the nesting of the sections.
</li>
<li>
The special <code>main</code> section holds the top-level keys (ones not in any section).
</li>
<li>
The function <code>std.manifestIni</code> (written in Jsonnet) serializes the structure to
a string.
</li>
<li>
Jsonnet is run in string output mode <tt>-S</tt> to print the string verbatim rather than
encoded as JSON.
</li>
</ul>
<p>
The code below could be executed with the command:
</p>
<pre>$ jsonnet -S ini.jsonnet</pre>
<p>
Try replacing <code>manifestIni</code>. with <code>manifestJson</code> or
<code>manifestPython</code>. There are many such functions in the standard library, and you
can write custom ones too. Finally, to see the effect of <tt>-S</tt> directly, try
replacing the whole buffer with just a string literal.
</p>
</div>
<div style="clear: both"></div>
</div>
</div>
<div class="inverse hgroup">
<div class=hgroup-inline>
<div class="tab-window-input" id="ini-input">
<div class="tab-header">
</div>
<textarea id=ini-jsonnet>
std.manifestIni({
main: {
a: 1,
},
sections: {
foo: {
a: true,
b: 2,
},
bar: {
x: 'text',
},
},
})
</textarea>
</div>
<div class="bigarrow">➡</div>
<div class="tab-window-output" id="ini-output">
<div class="tab-header">
<div class=selected onclick="tab_output_click(this, 'output-ini')">
output.ini
</div>
</div>
<textarea readonly class="selected code-json" id="output-ini">
a = 1
[bar]
x = text
[foo]
a = 1
b = 2
</textarea>
</div>
<script>
demo(
'ini-input',
{
'ini-jsonnet': 'ini.jsonnet',
},
'ini.jsonnet',
'ini-output',
false,
true
);
</script>
<div style="clear: both"></div>
</div>
</div>
<div class="hgroup">
<div class="hgroup-inline">
<div class="panel">
<p>
Thus, despite internally using the JSON data model, Jsonnet can provide config generation
for applications that consume INI.
</p>
<p>
Below is a more complex example where we construct an SVG file by emitting XML. It shows
how to write a custom manifestation function, and also a non-trivial example of representing
something in Jsonnet.
</p>
</div>
<div style="clear: both"></div>
</div>
</div>
<div class="inverse hgroup">
<div class=hgroup-inline>
<div class="tab-window-input" id="svg-input">
<div class="tab-header">
</div>
<textarea id=svg-jsonnet>
local xml = import 'xml.libsonnet';
local Svg = xml.Element('svg') {
height:: 100,
width:: 100,
};
local Polygon = xml.Element('polygon') {
points: std.join(
' ',
[
'%s,%s' % coords
for coords in self.pointCoords
]
),
};
local Path = xml.Element('path') {
cmds:: [],
d: std.join('', self.cmds),
};
local ref(element) = 'url(#%s)' % element.id;
local logo = Svg {
version: '1.1',
xmlns: 'http://www.w3.org/2000/svg',
x: 0,
y: 0,
width: 90,
height: 90,
viewBox: '0 0 180 180',
bg:: xml.Element('linearGradient') {
id: 'bg',
gradientUnits: 'userSpaceOnUse',
x1: 53,
y1: 19,
x2: 126,
y2: 162,
colour1:: '#0091AD',
colour2:: '#00728F',
local grad = self,
has: [
xml.Element('stop') {
offset: 0,
'stop-color': grad.colour1,
},
xml.Element('stop') {
offset: 1,
'stop-color': grad.colour2,
},
],
},
line:: Polygon {
fill: 'white',
pointCoords:: [
[0, 38],
[90, -14],
[180, 38],
[180, 142],
[90, 194],
[0, 142]
],
},
fill:: Path {
fill: ref($.bg),
cmds: [
'M168,49 v86 L129,82 L168,49 z',
'M90,98 l67-59 L90,0 V98 z',
'M73,10 L12,45 v90 l61,35 V10 z',
'M117,94 L90,116 v63 l63-36 L117,94 z',
],
},
has: [
$.bg,
$.line,
$.fill,
],
};
xml.manifestXmlObj(logo)
</textarea>
<textarea id=xml-libsonnet>
// Some utility functions for generating XML.
{
// Serialize a XML document represented as a
// Jsonnet value. Each node of the XML tree
// has a field 'tag', and optionally 'attrs'
// (an string mapping), and 'has', an array
// of children.
manifestXmlObj(value)::
local aux(v, cindent) =
if !std.isObject(v) then
error 'Expected a object, got %s'
% std.type(value)
else
local attrs = [
' %s="%s"' % [k, v.attrs[k]]
for k in std.objectFields(v.attrs)
];
if std.length(v.has) > 0 then
std.deepJoin([
cindent, '<', v.tag, attrs, '>\n',
[
aux(x, cindent + ' ')
for x in v.has
],
cindent, '</', v.tag, '>\n'
])
else
std.deepJoin([
cindent, '<', v.tag, attrs, '>',
'</', v.tag, '>\n']);
aux(value, ''),
// A convenience object whose attributes are
// taken from the top-level non-hidden fields
// of the object. This works except for
// attributes called 'tag' or 'has'.
Element(tag):: {
local element = self,
tag:: tag,
attrs:: {
[k]: element[k]
for k in std.objectFields(element)
},
has:: [],
},
}
</textarea>
</div>
<div class="bigarrow">➡</div>
<div class="tab-window-output" id="svg-output">
<div class="tab-header">
<div class=selected onclick="tab_output_click(this, 'output-svg')">
output.svg
</div>
</div>
<textarea readonly class="selected code-json" id="output-svg">
<svg version="1.1" viewBox="0 0 180 180" x="0" xmlns="http://www.w3.org/2000/svg" y="0">
<linearGradient gradientUnits="userSpaceOnUse" id="bg" x1="53" x2="126" y1="19" y2="162">
<stop offset="0" stop-color="#0091AD"></stop>
<stop offset="1" stop-color="#00728F"></stop>
</linearGradient>
<polygon fill="white" points="0,38 90,-14 180,38 180,142 90,194 0,142"></polygon>
<path d="M168,49 v86 L129,82 L168,49 zM90,98 l67-59 L90,0 V98 zM73,10 L12,45 v90 l61,35 V10 zM117,94 L90,116 v63 l63-36 L117,94 z" fill="url(#bg)"></path>
</svg>
</textarea>
</div>
<script>
demo(
'svg-input',
{
'svg-jsonnet': 'svg.jsonnet',
'xml-libsonnet': 'xml.libsonnet',
},
'svg.jsonnet',
'svg-output',
false,
true
);
</script>
<div style="clear: both"></div>
</div>
</div>
<div class="hgroup">
<div class="hgroup-inline">
<div class="panel">
<p>
This approach generalizes widely because JSON is very versatile. Indeed, its success as an
interchange format can be attributed to that versatility. Using the technique described
above, Jsonnet can output any format that can be represented in JSON.
</p>
</div>
<div style="clear: both"></div>
</div>
</div>
|