https://github.com/dnesting/lua-jsond
https://github.com/dnesting/lua-jsond
Last synced: 19 days ago
JSON representation
- Host: GitHub
- URL: https://github.com/dnesting/lua-jsond
- Owner: dnesting
- Created: 2025-05-03T16:49:05.000Z (9 months ago)
- Default Branch: main
- Last Pushed: 2025-05-25T13:12:39.000Z (9 months ago)
- Last Synced: 2025-05-25T13:47:49.136Z (9 months ago)
- Language: Lua
- Size: 38.1 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# jsond
This is a Lua library that implements JSON decoding while preserving Wireshark TvbRange references.
This allows you to use JSON fields as dissector fields, allowing the UI to maintain a reference to
the original packet data.
Status:
This is under active development and unstable.
It's also nearly unacceptably slow and its value is questionable.
# Usage
```lua
jsond = require("jsond")
json = '{"num_field": 42, "obj_field": {"obj_num": 43}, "array_field": ["one", "two"]}'
tvb_range = ByteArray.new(json, true):tvb()()
jsond = require("jsond")
data = jsond.decode(tvb_range) -- a Value
tree:add(field.num_field, data.num_field()) -- unpacks into (TvbRange, number)
tree:add(field.obj_num, data.obj_field.obj_num())
for _, val in ipairs(data.array_field) do
local first_letter = val:sub(1, 1) -- "o", "t" Values
tree:add(field.first_letters, first_letter())
end
```
# API Reference
Generally speaking, JSON types are decoded to standard Lua types,
and wrapped in a class that gives you access to the associated
[`TvbRange`](https://www.wireshark.org/docs/wsdg_html_chunked/lua_module_Tvb.html#lua_class_TvbRange)
and value.
## Functions
### `jsond.decode(tvbr)`
Decodes the JSON data contained in `tvbr`.
Returns a `Value` or raises an error.
```lua
buf = ByteArray.new("42", true):tvb()()
data = jsond.decode(buf) -- Number
print(data:range() == buf(0, 2))
print(type(data:val()), data:val())
-- true
-- number 42
```
```lua
buf = ByteArray.new("[42]", true):tvb()()
data = jsond.decode(buf) -- Array
elem = data[1] -- Number
print(elem:range() == buf(1, 2))
print(type(elem:val()), elem:val())
-- true
-- number 42
```
### `jsond.range(data)`
Returns the [`TvbRange`](https://www.wireshark.org/docs/wsdg_html_chunked/lua_module_Tvb.html#lua_class_TvbRange) that was the source for the `Value` held by `data`.
This will work for all classes in the tree returned by `jsond.decode`.
Some classes also implement a `value:range()` method as a convenience.
```lua
buf = ByteArray.new("42", true):tvb()()
data = jsond.decode(buf) -- Number
print(jsond.range(data) == buf(0, 2))
-- true
```
### `jsond.value(data)`
Returns the Lua value represented by `data`.
This will be `nil` only if the `Value` represents a JSON `null`.
Most classes also implement a `value:val()` method as a convenience.
Note that the aggregated types `Array` and `Object` are represented by a Lua table. An `Array`'s elements will be `Value` instances, as are an `Object's` keys and values.
```lua
buf = ByteArray.new("42", true):tvb()()
data = jsond.decode(buf) -- Number
print(jsond.value(data) == 42)
-- true
```
---
### `jsond.bool(data)`
Returns the Lua truthiness of the Lua value of `data`. Equivalent to `not not jsond.value(data)`.
Most classes also implement a `value:bool()` method as a convenience.
### `jsond.is_zero(data)`
Returns true if `data` is a number == 0, a false boolean, or an empty string, array, or object.
Most classes also implement a `value:nonzero()` method as a convenience, equivalent to `not jsond.is_zero(data)`.
```lua
buf = ByteArray.new('""', true):tvb()()
data = jsond.decode(buf) -- String
print(jsond.is_zero(data))
-- true
```
### `jsond.sorted(data [, comp])`
Returns an iterator over a sorted copy of `data`, which must be an `Array` or `Object`.
For an `Array`, this is equivalent to sorting its elements according to how the underlying Lua values compare to each other. If `comp` is provided, it will be provided these underlying values.
For an `Object`, the iterator yields keys and values, sorted by key. If `comp` is provided, it will be provided the Lua values for each key. The values yielded by the iterator will be `Value`s.
```lua
buf = ByteArray.new('{"b": 2, "a": 1}', true):tvb()()
data = jsond.decode(buf) -- Object
for k, v in jsond.sorted(data) do
print(k, v)
end
-- a 1
-- b 2
```
```lua
buf = ByteArray.new('["b", "a"]', true):tvb()()
data = jsond.decode(buf) -- Array
for i, v in jsond.sorted(data) do
print(i, v)
end
-- 1 a
-- 2 b
```
### `jsond.type(data)`
Returns the JSON type held by `data`.
This will be one of "string", "number", "boolean", "array", "object", "null",
or `nil` if data isn't a `Value`.
```lua
buf = ByteArray.new("42", true):tvb()()
data = jsond.decode(buf)
jsond.type(data) -- "number"
```
## Classes
```
Value
├── BasicValue
│ ├── Array
│ ├── Boolean
│ ├── Nil
│ ├── Number
│ └── String
└── Object
```
### `Value`
The `Value` class is the superclass of the other classes below.
It implements a few methods that are consistent between all classes.
```
Value
├── __call()
└── __string()
```
#### `__call()`
Every `Value` is callable, returning `(TvbRange, value)`.
This is designed to facilitate use in a WireShark tree.
The following two lines are equivalent:
```lua
tree:add(field.my_field, data())
tree:add(field.my_field, jsond.range(data), jsond.value(data))
```
#### `__string()`
Every `Value` supports `tostring(data)`, which is equivalent to
`tostring(jsond.value(data))`.
### `BasicValue` (`Value`)
```
Value
├── __call()
├── __string()
└── BasicValue
├── eq, ne, lt, le, gt, ge
├── bool() -- boolean
├── nonzero() -- boolean
├── range() -- TvbRange
├── raw() -- string
└── val() -- underlying Lua value
```
A `BasicValue` is the superclass for `Array`, `Boolean`, `Nil`, `Number`, and `String`, implementing shared convenience methods absent from `Object`.
### Comparators
A `BasicValue` has methods allowing for comparisons against other `BasicValues` as well as standard Lua types. For example:
```lua
buf = ByteArray.new("42", true):tvb()()
val = jsond.decode(buf)
print(val == 42) -- val is a Value, not a number
print(val:eq(42))
-- false
-- true
```
The following methods are implemented:
- `eq`, `ne`
- `lt`, `le`
- `gt`, `ge`
### `basic:bool()`
Returns the Lua truthiness of this value.
Equivalent to `jsond.bool(basic)`.
### `basic:nonzero()`
Returns true if the value is non-zero, non-false, non-empty, and non-nil.
Equivalent to `not jsond.is_zero(basic)`.
#### `basic:range()`
Returns the `TvbRange` corresponding to this value. Equivalent to `jsond.range(basic)`.
#### `basic:raw()`
Returns the raw JSON string for this value. Equivalent to `jsond.range(basic):raw()`.
#### `basic:val()`
Returns the Lua value corresponding to this value. Equivalent to `jsond.value(basic)`.
### `Boolean` (`BasicValue`)
`Boolean` implements no methods beyond those in `BasicValue`.
### `Nil` (`BasicValue`)
`Nil` implements no methods beyond those in `BasicValue` and always holds only a `nil` value (a JSON `null`).
### `Number` (`BasicValue`)
```
Value
├── __call()
├── __string()
└── BasicValue
├── eq, ne, lt, le, gt, ge
├── bool() -- boolean
├── nonzero() -- boolean
├── range() -- TvbRange
├── raw() -- string
├── val() -- number
└── Number
└── nstime() -- NSTime
```
#### `n:nstime()`
Interprets the number as the number of seconds since the Unix `time_t` epoch and returns an `NSTime`. Approximately equivalent to `NSTime.new(jsond.value(n))`,
but can handle fractional seconds as well.
### `String` (`BasicValue`)
```
Value
├── __call()
├── __string()
└── BasicValue
├── eq, ne, lt, le, gt, ge
├── bool() -- boolean
├── nonzero() -- boolean
├── range() -- TvbRange
├── raw() -- string
├── val() -- string
└── String
├── __len()
├── byte([i [, j]]) -- Number
├── ether() -- Address
├── ipv4() -- Address
├── ipv6() -- Address
├── lower() -- String
├── number([base]) -- Number
├── sub([first [, last])) -- String
└── upper() -- String
```
A `String` holds a string value.
#### `__len()`
`String` supports `#string`.
#### `s:byte([i [, j]])`
Returns one or more `Number` instances representing the Lua code point(s) for the character(s) at index `i` (defaults to 1) through `j`, similar to the standard `string.byte` function.
```lua
buf = ByteArray.new('"abc"'):tvb()()
s = jsond.decode(buf)
b = s:byte(2) -- Number
print(b:range() == buf(2, 1))
print(b:val())
-- true
-- 98
```
#### `s:ether()`
Equivalent to `Address.ether(jsond.value(s))` or `jsond.range(s):ether()`.
#### `s:ipv4()`
Equivalent to `Address.ipv4(jsond.value(s))` or `jsond.range(s):ipv4()`.
#### `s:ipv6()`
Equivalent to `Address.ipv6(jsond.value(s))` or `jsond.range(s):ipv6()`.
#### `s:lower()`
Returns a `String` containing a lower-cased copy of `s`.
#### `s:number([base])`
If string contains numeric digits, returns a `Number`, using the optional base.
#### `s:sub([first [, last]])`
Returns a `String` containing the substring starting from `first` (index 1) to `last`,
inclusive, similar to Lua's standard string `:sub()` method.
```lua
buf = ByteArray.new('"foobar"'):tvb()()
s = jsond.decode(buf)
b = s:sub(4, 6) -- String
print(b:range() == buf(4, 3))
print(type(b:val()), b:val())
-- true
-- string bar
```
#### `s:upper()`
Returns a `String` containing an upper-cased copy of the string.
### `Array` (`BasicValue`)
```
Value
├── __call()
├── __string()
└── BasicValue
├── eq, ne, lt, le, gt, ge
├── bool() -- boolean
├── nonzero() -- boolean
├── range() -- TvbRange
├── raw() -- string
├── val() -- table
└── Array
├── __len()
├── __pairs() -- Value
├── __index() -- Value
├── sorted([comp]) -- Array
└── sort()
```
An `Array` holds a `table` of `Value` instances,
indexed by numeric position.
#### `__len()`
An `Array` supports `#array`.
#### `__pairs()` and `__ipairs()`
An `Array` supports iteration using `pairs` and `ipairs`.
#### `__index()` and `__newindex()`
An `Array` supports `array[0]`-style indexing.
#### `a:sorted([comp])`
Returns an iterator over the sorted values of `a`. The `comp` comparator will be provided the underlying Lua values for each element.
#### `a:sort([comp])`
Performs an in-place sort of the array's `Value`s.
The `comp` comparator will be provided the underlying Lua values for each element.
### `Object` (`Value`)
```
Value
├── __call()
├── __string()
└── Object
├── __pairs() -- String, Value
└── __index() -- Value
```
An `Object` contains a mapping from `String` keys to `Value`s. It contains no methods in order to allow indexing of the underlying object's properties without risk of conflict.
Indexing supports indexing by `String`, or a Lua string. These references are equivalent:
```lua
buf = ByteArray.new('{"field": "value"}', true):tvb()()
obj = jsond.decode(buf) -- Object
for k, v in pairs(obj) do
print(obj[k] == obj["field"])
print(obj[k] == obj.field)
end
-- true
-- true
```