JSON Support in GoloScript
This comprehensive guide explains how to use JSON support in GoloScript, including the toJSON(), fromJSON(), and escapeJSON() functions.
Introduction
GoloScript provides native and comprehensive JSON support that allows you to:
- Encode Golo values to JSON with
toJSON() - Decode JSON to Golo objects with
fromJSON() - Escape strings manually with
escapeJSON()
JSON support automatically handles all Golo types (numbers, strings, booleans, null, arrays, maps, DynamicObjects) as well as complex nested structures.
Available Functions
toJSON(value)
Converts a Golo value to a JSON string.
let num = 42
let json = toJSON(num) # "42"
let obj = map[["name", "Alice"], ["age", 30]]
let json = toJSON(obj) # {"age":30,"name":"Alice"}
fromJSON(jsonString)
Parses a JSON string and returns a Golo object.
let json = "{\"name\":\"Bob\",\"age\":25}"
let obj = fromJSON(json)
println(obj: name()) # Bob
println(obj: age()) # 25
escapeJSON(string)
Manually escapes a string for use in JSON (newlines, quotes, etc.).
let text = """Line 1
Line 2"""
let escaped = escapeJSON(text) # "Line 1\nLine 2"
Note: toJSON() does this escaping automatically. Only use escapeJSON() when manually constructing JSON via templates.
Basic Usage
Encoding Simple Values
# Numbers
toJSON(42) # 42
toJSON(3.14) # 3.14
toJSON(-100) # -100
# Strings
toJSON("Hello") # "Hello"
toJSON("") # ""
# Booleans and null
toJSON(true) # true
toJSON(false) # false
toJSON(null) # null
Encoding Collections
# Simple array
let arr = [1, 2, 3, 4, 5]
toJSON(arr) # [1,2,3,4,5]
# Mixed type array
let mixed = [1, "hello", true, null, 3.14]
toJSON(mixed) # [1,"hello",true,null,3.14]
# Nested array
let nested = [[1, 2], [3, 4], [5, 6]]
toJSON(nested) # [[1,2],[3,4],[5,6]]
Encoding Objects
# Map
let person = map[["name", "Alice"], ["age", 30], ["city", "Paris"]]
toJSON(person) # {"age":30,"city":"Paris","name":"Alice"}
# DynamicObject
let obj = DynamicObject()
: name("Bob")
: age(25)
: active(true)
toJSON(obj) # {"active":true,"age":25,"name":"Bob"}
Decoding JSON
# Parse a number
let num = fromJSON("42") # 42
# Parse an array
let arr = fromJSON("[1,2,3]") # [1, 2, 3]
# Parse an object
let obj = fromJSON("{\"name\":\"Alice\",\"age\":30}")
println(obj: name()) # Alice
println(obj: age()) # 30
Round-trip (Encode then Decode)
# Create an object
let original = map[["title", "Golo"], ["version", 2.0]]
# Encode to JSON
let json = toJSON(original) # {"title":"Golo","version":2.0}
# Decode
let decoded = fromJSON(json)
println(decoded: title()) # Golo
println(decoded: version()) # 2.0
Supported Types
Primitive Types
| Golo Type | JSON Type | Example |
|---|---|---|
Integer |
number |
42 β 42 |
Float |
number |
3.14 β 3.14 |
String |
string |
"hello" β "hello" |
Boolean |
boolean |
true β true |
null |
null |
null β null |
Collections
| Golo Type | JSON Type | Example |
|---|---|---|
Array |
array |
[1, 2, 3] β [1,2,3] |
Map |
object |
map[["a", 1]] β {"a":1} |
DynamicObject |
object |
obj:a(1) β {"a":1} |
Nested Structures
GoloScript handles complex nested structures perfectly:
let config = DynamicObject()
: appName("MyApp")
: version("1.0.0")
: settings(DynamicObject()
: debug(true)
: port(8080)
)
: servers(["server1.com", "server2.com"])
toJSON(config)
# {"appName":"MyApp","servers":["server1.com","server2.com"],"settings":{"debug":true,"port":8080},"version":"1.0.0"}
Advanced Cases
Multiline Strings
toJSON() automatically handles multiline strings by escaping them correctly:
let poem = """Roses are red,
Violets are blue,
Golo is great!"""
toJSON(poem) # "Roses are red,\nViolets are blue,\nGolo is great!"
# Round-trip preserves the original content
let decoded = fromJSON(toJSON(poem))
println(decoded) # Displays the poem on 3 lines
Strings with Special Characters
let text = "He said \"Hello!\" to her"
toJSON(text) # "He said \"Hello!\" to her"
let special = "Tabs:\t\tNewlines:\n"
toJSON(special) # "Tabs:\t\tNewlines:\n"
Unicode Characters
let unicode = "Hello δΈη π"
toJSON(unicode) # "Hello δΈη π"
Array of Objects
let users = [
map[["id", 1], ["name", "Alice"]],
map[["id", 2], ["name", "Bob"]],
map[["id", 3], ["name", "Charlie"]]
]
let json = toJSON(users)
# [{"id":1,"name":"Alice"},{"id":2,"name":"Bob"},{"id":3,"name":"Charlie"}]
let decoded = fromJSON(json)
println(decoded: get(0): name()) # Alice
Template vs toJSON
There are two approaches to building JSON in GoloScript.
β Problem: Template without Escaping
let systemMsg = """You are a helpful assistant.
You can say "Hello!" """
let badTemplate = """{"message": "{{.Msg}}"}"""
let badData = DynamicObject(): Msg(systemMsg)
let badJSON = template(badTemplate, badData)
# β οΈ BROKEN: Newlines and quotes are not escaped!
β Solution 1: Template + escapeJSON()
import nova.Helpers
let goodData = DynamicObject(): Msg(escapeJSON(systemMsg))
let goodJSON = template(badTemplate, goodData)
# β VALID: String is properly escaped
Advantages:
- Simple: just wrap with
escapeJSON() - Familiar: continues using templates
- Explicit: you see whatβs being escaped
Disadvantages:
- Manual: must remember to escape each string
- Error-prone: easy to forget for new fields
β Solution 2: toJSON() (Recommended)
let request = DynamicObject()
: model("gpt-3.5")
: message(systemMsg) # β No escaping needed!
let json = toJSON(request)
# β VALID: Automatic escaping
Advantages:
- Automatic: impossible to forget escaping
- Type-safe: structure errors caught early
- Maintainable: easier to refactor
- No templates needed
- Better for complex nested structures
Disadvantages:
- Different syntax (objects instead of strings)
- Slightly more verbose for very simple cases
Recommendations
Use TEMPLATE + escapeJSON() when:
- You have existing templates to maintain
- Very simple, flat JSON structures
- You prefer string-based construction
- Template comes from an external source
Use toJSON() when:
- Building complex nested structures
- Working with APIs (AI, REST, etc.) β Recommended!
- You want automatic safety and type checking
- Structure may change/evolve over time
- You prefer object-oriented construction
π‘ General guideline: For new code, prefer toJSON() for better safety and maintainability, especially for API requests.
Practical Examples
Example 1: Application Configuration
let config = DynamicObject()
: appName("MyGoloApp")
: version("1.0.0")
: database(DynamicObject()
: host("localhost")
: port(5432)
: name("mydb")
)
: features(["auth", "logging", "metrics"])
: debug(true)
# Save to JSON
let configJSON = toJSON(config)
writeFile("config.json", configJSON)
# Load from JSON
let loadedConfig = fromJSON(readFile("config.json"))
println(loadedConfig: database(): host()) # localhost
Example 2: AI API Request
let buildAIRequest = |model, systemPrompt, userMessage| {
return DynamicObject()
: model(model)
: temperature(0.7)
: messages([
DynamicObject(): role("system"): content(systemPrompt),
DynamicObject(): role("user"): content(userMessage)
])
: stream(false)
}
let systemPrompt = """You are a helpful coding assistant.
You can answer questions about programming.
Always provide clear examples."""
let request = buildAIRequest(
"gpt-4",
systemPrompt,
"How do I use JSON in Golo?"
)
let requestJSON = toJSON(request)
# Send via HTTP...
Example 3: Structured Data for REST API
let createUserPayload = |name, email, roles| {
return DynamicObject()
: name(name)
: email(email)
: roles(roles)
: active(true)
: metadata(DynamicObject()
: createdAt(nowAsISO())
: source("api")
)
}
let newUser = createUserPayload(
"Alice Johnson",
"alice@example.com",
["user", "editor"]
)
let payload = toJSON(newUser)
# POST to /api/users with payload
Example 4: Parse API Response
let responseJSON = """
{
"status": "success",
"data": {
"users": [
{"id": 1, "name": "Alice", "active": true},
{"id": 2, "name": "Bob", "active": false}
],
"total": 2
}
}
"""
let response = fromJSON(responseJSON)
println("Status:", response: status())
println("Total:", response: data(): total())
# Iterate over users
let users = response: data(): users()
foreach user in users {
println("User:", user: name(), "- Active:", user: active())
}
Best Practices
β Do
-
Use toJSON() for new code
let data = DynamicObject(): name("Alice"): age(30) let json = toJSON(data) # Safe and automatic -
Check values after fromJSON()
let obj = fromJSON(jsonString) if obj: isNotNull() { println(obj: name()) } -
Use helper functions for common structures
function createAPIRequest = |endpoint, method, body| { return DynamicObject() : endpoint(endpoint) : method(method) : body(body) } -
Handle parsing errors
try { let obj = fromJSON(jsonString) # Use obj... } catch (e) { println("JSON parsing error:", e: getMessage()) }
β Donβt
-
Donβt forget to escape with templates
# β Bad let data = DynamicObject(): msg(multilineText) let json = template(tpl, data) # Broken if multilineText has newlines! # β Good let data = DynamicObject(): msg(escapeJSON(multilineText)) let json = template(tpl, data) # β β Even better let obj = DynamicObject(): message(multilineText) let json = toJSON(obj) # Automatic! -
Donβt parse invalid JSON without checking
# β Bad let obj = fromJSON(untrustedInput) # May throw exception # β Good try { let obj = fromJSON(untrustedInput) } catch (e) { # Handle error } -
Donβt manually build JSON with concatenation
# β Bad let json = "{\"name\":\"" + name + "\",\"age\":" + age + "}" # β Good let obj = DynamicObject(): name(name): age(age) let json = toJSON(obj)
Summary
JSON support in GoloScript is:
- Complete: Handles all Golo types and nested structures
- Automatic:
toJSON()automatically escapes special characters - Bidirectional:
toJSON()andfromJSON()for encoding/decoding - Safe: Type-safe with DynamicObjects
- Practical: Perfect for APIs, configurations, and data exchange
For most use cases, prefer toJSON() for its safety and simplicity.
Β© 2026 GoloScript Project | Built with Gu10berg
Subscribe: π‘ RSS | βοΈ Atom