Working with JSON
The value your Do callback receives is backed by JSON. Most of the time it behaves like an ordinary Lua table and you can ignore that it is JSON underneath. This page covers the cases where the JSON nature shows through: the methods on the value, iterating it, and the few places where a plain Lua table is not enough and you use the J helper.
It mostly acts like a table
Read a field, assign a field, nest tables. This needs no special API:
part.mass = 0.0
body.MineDustColor = { x = 0.53, y = 0.39, z = 0.29, w = 1.0 }
node.TechTreePosition = { x = 1600.0, y = 750.0 }Working with lists
A JSON array exposes methods for the things plain index assignment does not cover. The common ones:
:Append(value)adds an element to the end.:Insert(index, value)inserts at a 1-indexed position, shifting later elements right.:RemoveAt(index)removes the element at a 1-indexed position.:RemoveWhere(predicate)removes every element the predicate returns true for.:Clear()empties the list.:Countis the number of elements.
Appending to a list is extremely common. This real patch adds a flavor-description entry to an experiment's list:
experiment.DataFlavorDescriptions:Append({
ResearchLocationID = "Pol_Landed_PolMidlands",
LocalizationTag = "Science/Experiments/CrewObservation/Data/Flavor/Pol_Landed_PolMidlands"
})RemoveWhere takes a predicate. This patch removes one part id from a tech node's unlocked list:
node.UnlockedPartsIDs:RemoveWhere(|id| id == "booster_1v_solid_kickback")You can also replace a whole list by assigning a Lua array:
node.UnlockedPartsIDs = { "dockingport_0v_inline", "dockingport_1v_inline" }Working with objects
A JSON object (string-keyed) exposes:
:HasKey(key)tests for a key.:Keys()returns the property names.:Remove(key)deletes a property.:Patch(key, callback)calls your callback with the value atkey, but only if the key is present. This is a tidy way to edit something that might not be there without anifguard.
data:Patch("SomeOptionalField", function(value)
-- only runs if the field exists
end)Iterating
pairs and ipairs both work on the value. Arrays yield index and value, objects yield key and value. This real patch walks a part's drag cubes:
for _, cube in pairs(data.cubes) do
if cube.Name == "DEPLOYED" then
-- ... edit this cube ...
end
endThe J helper: integers and empty arrays
Lua has one number type and treats every number as a float. JSON distinguishes integers from floats, and a few game fields insist on an integer. Usually, the JSON wrapper will try and match type when writing to a slot, but when you need to write an integer into a slot that is not already filled with an integer (usually in the case of a new slot), wrap it with J.Int:
someField = J.Int(5)The other gap is the empty collection. An empty Lua table {} is ambiguous: it could become an empty object or an empty array, and the default is an object. When a field needs an empty array specifically, use J.Empty():
someList = J.Empty()You can also call J(value) to convert a Lua value into the JSON-backed form explicitly, which you rarely need but is there for the cases where the automatic conversion does not do what you want.
Rule of thumb
Write plain Lua tables and assignments by default. Use the list and object methods when you are adding to, removing from, or iterating a collection. Use J.Int and J.Empty only when a specific field rejects what the automatic conversion produced. If a number that should be a whole number is showing up wrong in game, or is throwing an error on deserialization, an integer slot needing J.Int is the most likely cause.