Merge & Clone
zon.zig supports merging documents together and creating deep copies.
Merging Documents
Use merge to combine two documents. Values from the source document overwrite existing values in the target.
zig
var base = zon.create(allocator);
defer base.deinit();
try base.setString("name", "myapp");
try base.setString("version", "1.0.0");
try base.setInt("port", 8080);
var override = zon.create(allocator);
defer override.deinit();
try override.setInt("port", 9000);
try override.setBool("debug", true);
// Merge override into base (Shallow at the root level)
try base.merge(&override);
// base now has:
// - name: "myapp" (unchanged)
// - version: "1.0.0" (unchanged)
// - port: 9000 (overwritten)
// - debug: true (added)Recursive Merge
mergeRecursive combines nested structures (objects) instead of just overwriting the top-level keys. This is much more powerful for complex configurations.
zig
var base = try zon.parse(allocator, ".{ .db = .{ .port = 5432, .host = \"localhost\" } }");
defer base.deinit();
var override = try zon.parse(allocator, ".{ .db = .{ .port = 6000 } }");
defer override.deinit();
try base.mergeRecursive(&override);
// base now has:
// .db = .{ .port = 6000, .host = "localhost" }Deep Equality
Use eql to check if two documents or values are deeply equivalent.
zig
if (base.eql(&override)) {
std.debug.print("Full documents are identical\n", .{});
}Use Cases
- Configuration Layers: Merge environment-specific configs over base configs
- Default Values: Create defaults, then merge user preferences
- Incremental Updates: Apply patches to existing documents
Cloning Documents
Use clone to create an independent deep copy:
zig
var original = zon.create(allocator);
defer original.deinit();
try original.setString("name", "original");
try original.setInt("value", 100);
// Create deep copy
var copy = try original.clone();
defer copy.deinit();
// Modify the copy
try copy.setString("name", "copy");
try copy.setInt("value", 200);
// Original is unchanged
std.debug.print("Original: {s}, {d}\n", .{
original.getString("name").?,
original.getInt("value").?
});
// Output: Original: original, 100
std.debug.print("Copy: {s}, {d}\n", .{
copy.getString("name").?,
copy.getInt("value").?
});
// Output: Copy: copy, 200Practical Example
zig
const std = @import("std");
const zon = @import("zon");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
zon.disableUpdateCheck();
// Create base config
var dev_config = zon.create(allocator);
defer dev_config.deinit();
try dev_config.setString("environment", "development");
try dev_config.setString("database.host", "localhost");
try dev_config.setInt("database.port", 5432);
try dev_config.setString("database.name", "myapp_dev");
try dev_config.setBool("debug", true);
std.debug.print("Development config:\n", .{});
const dev_str = try dev_config.toString();
defer allocator.free(dev_str);
std.debug.print("{s}\n\n", .{dev_str});
// Clone for production
var prod_config = try dev_config.clone();
defer prod_config.deinit();
// Create production overrides
var prod_overrides = zon.create(allocator);
defer prod_overrides.deinit();
try prod_overrides.setString("environment", "production");
try prod_overrides.setString("database.host", "db.example.com");
try prod_overrides.setString("database.name", "myapp_prod");
try prod_overrides.setBool("debug", false);
// Merge overrides
try prod_config.merge(&prod_overrides);
std.debug.print("Production config:\n", .{});
const prod_str = try prod_config.toString();
defer allocator.free(prod_str);
std.debug.print("{s}\n", .{prod_str});
}Output:
Development config:
.{
.database = .{
.host = "localhost",
.name = "myapp_dev",
.port = 5432,
},
.debug = true,
.environment = "development",
}
Production config:
.{
.database = .{
.host = "db.example.com",
.name = "myapp_prod",
.port = 5432,
},
.debug = false,
.environment = "production",
}