DataUtils | Comprehensive Table and JSON Utilities

Overview

This module provides enhanced JSON and table manipulation utilities. It extends Roblox’s table and JSON handling with additional functions. Made this for a project and decided to extend it and release it :slight_smile:

Source File DataUtils.luau (14.6 KB)
Model File DataUtils.rbxm (7.3 KB)

Features

  • Deep Copy: Create complete independent copies of tables.

  • Merge Tables: Merge two tables with optional deep merge and conflict resolution.

  • Advanced JSON Serialization: Custom type handling for JSON encoding and decoding.

  • Nested Value Handling: Get and set nested table values using dot notation.

  • Table Filtering and Mapping: Filter and transform table values using custom functions.

  • JSON Schema Validation: Validate JSON data against a predefined schema.

  • Data Compression and Encryption: Secure and efficient data storage/transmission methods.

  • Data Diff and Patch: Track changes between data states and apply selective updates.

  • Data Pipeline: Create complex validation and transformation workflows.

  • Data Migration: Manage schema changes and data migrations.

  • Custom Serialization: Serialize and deserialize complex data types.

  • Table Flattening: Convert nested tables to flat key-value pairs and vice versa.

  • Table Comparison: Compare two tables and return a list of differences.

  • Table Sorting: Sort table values based on a custom comparison function.

Installation

To use the DataUtils module, simply require the module in your script:


local DataUtils = require(path.to.DataUtils)

Usage

Deep Copy

Create a deep copy of a table:


local original = { a = 1, b = { c = 2 } }

local copy = DataUtils.DeepCopy(original)

Merge Tables

Merge two tables with optional deep merge and conflict resolution:


local table1 = { a = 1, b = { c = 2 } }

local table2 = { b = { d = 3 }, e = 4 }

local merged = DataUtils.MergeTables(table1, table2, true, true)

Advanced JSON Serialization

Encode and decode JSON with custom type handling:


local data = { position = Vector3.new(1, 2, 3) }

local jsonString = DataUtils.AdvancedEncode(data, { handleSpecialTypes = true })

local decodedData = DataUtils.JSONToTable(jsonString, { convertRobloxTypes = true })

Nested Value Handling

Get and set nested table values using dot notation:


local tbl = { a = { b = { c = 1 } } }

local value = DataUtils.GetNestedValue(tbl, "a.b.c")

DataUtils.SetNestedValue(tbl, "a.b.d", 2)

Data Compression and Encryption

Compress and decompress data:


local data = { a = 1, b = 2 }

local compressed = DataUtils.Compress(data)

local decompressed = DataUtils.Decompress(compressed)

Data Diff and Patch

Create a diff and apply a patch:


local original = { a = 1, b = 2 }

local modified = { a = 1, b = 3, c = 4 }

local diff = DataUtils.CreateDataDiff(original, modified)

local patched = DataUtils.ApplyDataPatch(original, diff)

Data Pipeline

Create and process a data pipeline:


local pipeline = DataUtils.CreateDataPipeline()

:AddValidator(function(data) return true end)

:AddTransformer(function(data) return data end)

local result = pipeline:Process({ a = 1 })

Data Migration

Migrate data with schema changes:


local data = { __version = 1, a = 1 }

local migrations = {

[2] = function(data) data.b = 2 return data end

}

local migratedData = DataUtils.MigrateData(data, migrations)

Exemple Script

local DataUtils = require(script.Parent.DataUtils)

-- Example 1: DeepCopy - Creating Independent Table Copies
local originalTable = {
	name = "Player",
	stats = {
		health = 100,
		level = 5
	}
}
local copiedTable = DataUtils.DeepCopy(originalTable)
copiedTable.stats.health = 90  -- Modifying the copy doesn't affect the original

-- Example 2: MergeTables - Combining Tables with Advanced Options
local baseConfig = {
	graphics = {
		resolution = "1080p",
		brightness = 50
	},
	sound = {
		volume = 0.7
	}
}
local userConfig = {
	graphics = {
		brightness = 60
	},
	controls = {
		sensitivity = 5
	}
}
-- Deep merge with overwrite
local mergedConfig = DataUtils.MergeTables(baseConfig, userConfig, true, true)

-- Example 3: AdvancedEncode - Custom JSON Serialization
local playerData = {
	name = "AdventurerX",
	position = Vector3.new(10, 20, 30),
	inventory = {"sword", "shield", "potion"}
}
local encodedData = DataUtils.AdvancedEncode(playerData, {
	handleSpecialTypes = true
})

-- Example 4: GetNestedValue and SetNestedValue - Navigating Complex Tables
local gameState = {
	players = {
		player1 = {
			stats = {
				level = 10,
				experience = 500
			}
		}
	}
}
-- Retrieve nested value
local playerLevel = DataUtils.GetNestedValue(gameState, "players.player1.stats.level")
-- Set nested value
DataUtils.SetNestedValue(gameState, "players.player1.stats.experience", 750)

-- Example 5: FilterTable - Selective Data Extraction
local items = {
	sword = { damage = 20, durability = 80 },
	shield = { damage = 0, durability = 90 },
	axe = { damage = 15, durability = 70 }
}
-- Filter items with high durability
local robustItems = DataUtils.FilterTable(items, function(item)
	return item.durability > 75
end)

-- Example 6: Validation with Schema Checking
local userSchema = {
	username = "string",
	age = "number",
	email = "string"
}
local validUser = {
	username = "GamePlayer123",
	age = 25,
	email = "player@example.com"
}
local isValid, errorMessage = DataUtils.ValidateSchema(validUser, userSchema)

-- Example 7: JSON Conversion with Roblox Types
local vector = Vector3.new(5, 10, 15)
local jsonString = DataUtils.TableToJSON({
	position = vector,
	timestamp = os.time()
})
local reconstructedData = DataUtils.JSONToTable(jsonString)

-- Example 8: Data Pipeline for Complex Transformations
local dataPipeline = DataUtils.CreateDataPipeline()
dataPipeline
	:AddValidator(function(data)
		-- Ensure age is positive
		return data.age > 0, "Age must be positive"
	end)
	:AddTransformer(function(data)
		-- Normalize username
		data.username = data.username:lower()
		return data
	end)

local processedData = dataPipeline:Process({
	username = "PlayerName",
	age = 30
})

-- Example 9: Data Diff and Patch
local originalData = {
	player = {
		name = "Hero",
		level = 5
	}
}
local modifiedData = {
	player = {
		name = "Hero",
		level = 6,
		experience = 1000
	}
}
local dataDiff = DataUtils.CreateDataDiff(originalData, modifiedData)
local patchedData = DataUtils.ApplyDataPatch(originalData, dataDiff)

-- Example 10: Compression and Serialization
local sensitiveData = {
	apiKey = "secret123",
	userCredentials = {
		username = "admin"
	}
}
local compressedData = DataUtils.Compress(sensitiveData)
local decompressedData = DataUtils.Decompress(compressedData)

--Print everything
print("DeepCopy:")
print(copiedTable)
print("mergedConfig")
print(mergedConfig)
print("encodedData:")
print(encodedData)
print("playerLevel:")
print(playerLevel)
print("robustItems:")
print(robustItems)
print("isValid:")
print(isValid)
print("errorMessage:")
print(errorMessage)
print("jsonString:")
print(jsonString)
print("reconstructedData:")
print(reconstructedData)
print("processedData:")
print(processedData)
print("dataDiff:")
print(dataDiff)
print("patchedData:")
print(patchedData)
print("compressedData:")
print(compressedData)
print("decompressedData:")
print(decompressedData)
5 Likes

Very cool! I might use this in my game.

1 Like