<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://linguifex.com/w/index.php?action=history&amp;feed=atom&amp;title=Module%3AJSON</id>
	<title>Module:JSON - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://linguifex.com/w/index.php?action=history&amp;feed=atom&amp;title=Module%3AJSON"/>
	<link rel="alternate" type="text/html" href="https://linguifex.com/w/index.php?title=Module:JSON&amp;action=history"/>
	<updated>2026-04-17T09:34:43Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.43.6</generator>
	<entry>
		<id>https://linguifex.com/w/index.php?title=Module:JSON&amp;diff=423787&amp;oldid=prev</id>
		<title>Sware: Created page with &quot;local export = {}  local m_table = require(&quot;Module:table&quot;)  local codepoint = require(&quot;Module:string utilities&quot;).codepoint local concat = table.concat local converter -- forward declaration local format = string.format local getmetatable = getmetatable local index_ipairs = m_table.indexIpairs local insert = table.insert local is_array = m_table.isArray local is_finite_real_number = require(&quot;Module:math&quot;).is_finite_real_number local is_utf8 = mw.ustring.isutf8 local pairs...&quot;</title>
		<link rel="alternate" type="text/html" href="https://linguifex.com/w/index.php?title=Module:JSON&amp;diff=423787&amp;oldid=prev"/>
		<updated>2025-01-11T13:02:07Z</updated>

		<summary type="html">&lt;p&gt;Created page with &amp;quot;local export = {}  local m_table = require(&amp;quot;Module:table&amp;quot;)  local codepoint = require(&amp;quot;Module:string utilities&amp;quot;).codepoint local concat = table.concat local converter -- forward declaration local format = string.format local getmetatable = getmetatable local index_ipairs = m_table.indexIpairs local insert = table.insert local is_array = m_table.isArray local is_finite_real_number = require(&amp;quot;Module:math&amp;quot;).is_finite_real_number local is_utf8 = mw.ustring.isutf8 local pairs...&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;local export = {}&lt;br /&gt;
&lt;br /&gt;
local m_table = require(&amp;quot;Module:table&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
local codepoint = require(&amp;quot;Module:string utilities&amp;quot;).codepoint&lt;br /&gt;
local concat = table.concat&lt;br /&gt;
local converter -- forward declaration&lt;br /&gt;
local format = string.format&lt;br /&gt;
local getmetatable = getmetatable&lt;br /&gt;
local index_ipairs = m_table.indexIpairs&lt;br /&gt;
local insert = table.insert&lt;br /&gt;
local is_array = m_table.isArray&lt;br /&gt;
local is_finite_real_number = require(&amp;quot;Module:math&amp;quot;).is_finite_real_number&lt;br /&gt;
local is_utf8 = mw.ustring.isutf8&lt;br /&gt;
local pairs = pairs&lt;br /&gt;
local pcall = pcall&lt;br /&gt;
local sorted_pairs = m_table.sortedPairs&lt;br /&gt;
local type = type&lt;br /&gt;
local ugsub = mw.ustring.gsub&lt;br /&gt;
&lt;br /&gt;
-- Given a finite real number x, returns a string containing its JSON&lt;br /&gt;
-- representation, with enough precision that it *should* round-trip correctly&lt;br /&gt;
-- (depending on the well-behavedness of the system on the other end).&lt;br /&gt;
local function json_fromNumber(x, level)&lt;br /&gt;
	if is_finite_real_number(x) then&lt;br /&gt;
		return format(&amp;quot;%.17g&amp;quot;, x)&lt;br /&gt;
	end&lt;br /&gt;
	error(format(&amp;quot;Cannot encode non-finite real number %g&amp;quot;, x), level)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local escape_char_map = {&lt;br /&gt;
	[&amp;quot;\b&amp;quot;] = &amp;quot;\\b&amp;quot;,&lt;br /&gt;
	[&amp;quot;\t&amp;quot;] = &amp;quot;\\t&amp;quot;,&lt;br /&gt;
	[&amp;quot;\n&amp;quot;] = &amp;quot;\\n&amp;quot;,&lt;br /&gt;
	[&amp;quot;\f&amp;quot;] = &amp;quot;\\f&amp;quot;,&lt;br /&gt;
	[&amp;quot;\r&amp;quot;] = &amp;quot;\\r&amp;quot;,&lt;br /&gt;
	[&amp;quot;\&amp;quot;&amp;quot;] = &amp;quot;\\\&amp;quot;&amp;quot;,&lt;br /&gt;
	[&amp;quot;\\&amp;quot;] = &amp;quot;\\\\&amp;quot;,&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local function escape_codepoint_utf16(c)&lt;br /&gt;
	if c &amp;gt;= 0x10000 then&lt;br /&gt;
		c = c - 0x10000&lt;br /&gt;
		return format(&amp;quot;\\u%04x\\u%04x&amp;quot;, 0xD800 + (c / 1024), 0xDC00 + (c % 1024))&lt;br /&gt;
	end&lt;br /&gt;
	return format(&amp;quot;\\u%04x&amp;quot;, c)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function escape_char(c)&lt;br /&gt;
	return escape_char_map[c] or escape_codepoint_utf16(codepoint(c))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Given a string, escapes any illegal characters and wraps it in double-quotes.&lt;br /&gt;
-- Raises an error if the string is not valid UTF-8.&lt;br /&gt;
local function json_fromString(s, ascii, level)&lt;br /&gt;
	if not is_utf8(s) then&lt;br /&gt;
		error(format(&amp;quot;Cannot encode non-UTF-8 string &amp;#039;%s&amp;#039;&amp;quot;, s), level)&lt;br /&gt;
	elseif ascii then&lt;br /&gt;
		-- U+0080 = \194\128 in UTF-8, U+10FFFF = \244\143\191\191 in UTF-8&lt;br /&gt;
		s = ugsub(s, &amp;#039;[%z\1-\31&amp;quot;\\\194\128-\244\143\191\191]&amp;#039;, escape_char)&lt;br /&gt;
	else&lt;br /&gt;
		-- U+2029 (LINE SEPARATOR, \226\128\168 in UTF-8)&lt;br /&gt;
		-- and U+2028 (PARAGRAPH SEPARATOR, \226\128\169 in UTF-8) are allowed&lt;br /&gt;
		-- in JSON, but must be escaped for compatibility with JavaScript.&lt;br /&gt;
		s = ugsub(s, &amp;#039;[%z\1-\31&amp;quot;\\\226\128\168\226\128\169]&amp;#039;, escape_char)&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;#039;&amp;quot;&amp;#039; .. s .. &amp;#039;&amp;quot;&amp;#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function json_fromTable(t, opts, current, level)&lt;br /&gt;
	local ret, open, close = {}&lt;br /&gt;
	if is_array(t) then&lt;br /&gt;
		for key, value in index_ipairs(t) do&lt;br /&gt;
			ret[key] = converter(value, opts, current, level + 1) or &amp;quot;null&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
		open, close = &amp;quot;[&amp;quot;, &amp;quot;]&amp;quot;&lt;br /&gt;
	else&lt;br /&gt;
		-- `seen_keys` memoizes keys already seen, to prevent collisions (e.g. 1&lt;br /&gt;
		-- and &amp;quot;1&amp;quot;).&lt;br /&gt;
		local seen_keys, colon, ascii = {}, opts.compress and &amp;quot;:&amp;quot; or &amp;quot; : &amp;quot;, opts.ascii&lt;br /&gt;
		for key, value in (opts.sort_keys and sorted_pairs or pairs)(t) do&lt;br /&gt;
			local key_type = type(key)&lt;br /&gt;
			if key_type == &amp;quot;number&amp;quot; then&lt;br /&gt;
				key = json_fromNumber(key, level + 1)&lt;br /&gt;
			elseif key_type ~= &amp;quot;string&amp;quot; then&lt;br /&gt;
				error(format(&amp;quot;Cannot use type &amp;#039;%s&amp;#039; as a table key&amp;quot;, key_type), level)&lt;br /&gt;
			end&lt;br /&gt;
			key = json_fromString(key, ascii, level + 1)&lt;br /&gt;
			if seen_keys[key] then&lt;br /&gt;
				error(format(&amp;quot;Collision for JSON key %s&amp;quot;, key), level)&lt;br /&gt;
			end&lt;br /&gt;
			seen_keys[key] = true&lt;br /&gt;
			insert(ret, key .. colon .. (converter(value, opts, current, level + 1) or &amp;quot;null&amp;quot;))&lt;br /&gt;
		end&lt;br /&gt;
		open, close = &amp;quot;{&amp;quot;, &amp;quot;}&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	ret = open .. (&lt;br /&gt;
		opts.compress and concat(ret, &amp;quot;,&amp;quot;) .. close or&lt;br /&gt;
		&amp;quot; &amp;quot; .. concat(ret, &amp;quot;, &amp;quot;) .. (&lt;br /&gt;
			#ret == 0 and &amp;quot;&amp;quot; or &amp;quot; &amp;quot;&lt;br /&gt;
		) .. close&lt;br /&gt;
	)&lt;br /&gt;
	current[t] = nil&lt;br /&gt;
	return ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function converter(this, opts, current, level) -- local declared above&lt;br /&gt;
	local val_type = type(this)&lt;br /&gt;
	if val_type == &amp;quot;nil&amp;quot; then&lt;br /&gt;
		return &amp;quot;null&amp;quot;&lt;br /&gt;
	elseif val_type == &amp;quot;boolean&amp;quot; then&lt;br /&gt;
		return this and &amp;quot;true&amp;quot; or &amp;quot;false&amp;quot;&lt;br /&gt;
	elseif val_type == &amp;quot;number&amp;quot; then&lt;br /&gt;
		return json_fromNumber(this, level + 1)&lt;br /&gt;
	elseif val_type == &amp;quot;string&amp;quot; then&lt;br /&gt;
		return json_fromString(this, opts.ascii, level + 1)&lt;br /&gt;
	elseif val_type ~= &amp;quot;table&amp;quot; then&lt;br /&gt;
		error(format(&amp;quot;Cannot encode type &amp;#039;%s&amp;#039;&amp;quot;, val_type), level)&lt;br /&gt;
	elseif current[this] then&lt;br /&gt;
		error(&amp;quot;Cannot use recursive tables&amp;quot;, level)&lt;br /&gt;
	end&lt;br /&gt;
	-- Memoize the table to enable recursion checking.&lt;br /&gt;
	current[this] = true&lt;br /&gt;
	if opts.ignore_toJSON then&lt;br /&gt;
		return json_fromTable(this, opts, current, level + 1)&lt;br /&gt;
	end&lt;br /&gt;
	-- Check if a toJSON method can be used. Use the lua_table flag to get a Lua&lt;br /&gt;
	-- table, as any options need to be applied to the output.&lt;br /&gt;
	local to_json = this.toJSON&lt;br /&gt;
	if to_json == nil then&lt;br /&gt;
		return json_fromTable(this, opts, current, level + 1)&lt;br /&gt;
	end&lt;br /&gt;
	local to_json_type = type(to_json)&lt;br /&gt;
	-- If it&amp;#039;s a function, call it.&lt;br /&gt;
	if to_json_type == &amp;quot;function&amp;quot; then&lt;br /&gt;
		local ret = converter(to_json(this, {lua_table = true}), opts, current, level + 1)&lt;br /&gt;
		current[this] = nil&lt;br /&gt;
		return ret&lt;br /&gt;
	-- If it&amp;#039;s a table and there&amp;#039;s a metatable, try to call it. If getmetatable&lt;br /&gt;
	-- returns nil, there&amp;#039;s definitely no metatable (so it can&amp;#039;t be callable),&lt;br /&gt;
	-- but otherwise the metatable could be protected with __metatable, so the&lt;br /&gt;
	-- only reliable approach is to call it with pcall.&lt;br /&gt;
	elseif to_json_type == &amp;quot;table&amp;quot; and getmetatable(to_json) ~= nil then&lt;br /&gt;
		local success, new = pcall(to_json, this, {lua_table = true})&lt;br /&gt;
		if success then&lt;br /&gt;
			local ret = converter(new, opts, current, level + 1)&lt;br /&gt;
			current[this] = nil&lt;br /&gt;
			return ret&lt;br /&gt;
		-- The error message will only take this exact form if it was thrown due&lt;br /&gt;
		-- to `this` not being callable, as it will contain a traceback if&lt;br /&gt;
		-- thrown in some other function, so raise the error if it&amp;#039;s not a&lt;br /&gt;
		-- match, since it&amp;#039;s an error elsewhere.&lt;br /&gt;
		elseif new ~= &amp;quot;attempt to call a table value&amp;quot; then&lt;br /&gt;
			error(new)&lt;br /&gt;
		end&lt;br /&gt;
		-- Not a callable table.&lt;br /&gt;
	end&lt;br /&gt;
	-- Treat as a conventional value.&lt;br /&gt;
	return json_fromTable(this, opts, current, level + 1)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- This function makes an effort to convert an arbitrary Lua value to a string&lt;br /&gt;
-- containing a JSON representation of it. It&amp;#039;s not intended to be very robust,&lt;br /&gt;
-- but may be useful for prototyping.&lt;br /&gt;
function export.toJSON(this, opts)&lt;br /&gt;
	return converter(this, opts == nil and {} or opts, {}, 3)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return export&lt;/div&gt;</summary>
		<author><name>Sware</name></author>
	</entry>
</feed>