Skip to content
This repository was archived by the owner on Jul 19, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 71 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
# Serialization
Unmanaged library for working with JSON and XML using readers and writers with bytes directly.
As well as intermediary types for representing objects within the supported formats.

### JSON Reader and writer
The reader and writers are used to iteratively progress over data. How the data
is stored should be known ahead of time (and can be tested).
Native library for working with common human readable formats using readers and writers.
With high-level types available for representing objects.

### Supported formats

- JSON
- JSON 5 (named after ECMAScript 5)
- XML

### JSON reader and writer

The reader and writers are low-level concepts used to traverse and write data:
```cs
using JSONWriter writer = new();
writer.WriteStartObject();
Expand All @@ -18,9 +25,10 @@ ReadOnlySpan<char> propertyValue = reader.ReadText(out ReadOnlySpan<char> proper
reader.ReadEndObject();
```

### Generic JSON Object
This is an alternative type thats able to represent a JSON object without the need
for interacting with the reader or writer.
### Generic JSON object

This is the high-level type that represents a JSON object without the need
for interacting with the reader/writer types:
```cs
JSONObject fruit = new();
fruit.Add("name", "cherry");
Expand All @@ -39,48 +47,56 @@ jsonObject.Add("inventory", inventory);

jsonObject["age"].Number++;

using UnmanagedList<char> buffer = new();
jsonObject.ToString(buffer, " ", true, true);
ReadOnlySpan<char> jsonText = buffer.AsSpan();
Console.WriteLine(jsonText.ToString());
using Text jsonText = new();
SerializationSettings settings = SerializationSettings.PrettyPrint;
jsonObject.ToString(jsonText, settings);
Console.WriteLine(jsonText);
```

Output:
```json
{
"name":"John Doe",
"age":43,
"alive":true,
"inventory":[
"name": "John Doe",
"age": 43,
"alive": true,
"inventory": [
"apples",
"oranges",
{
"name":"cherry",
"color":"red"
"name": "cherry",
"color": "red"
}
]
}
```

### JSON 5 support

The reading mechanism supports both old and new JSON formats. But for the writer,
some settings need to be adjusted:
```cs
SerializationSettings settings = new();
settings.flags |= SerializationFlags.QuotelessNames;
settings.flags |= SerializationFlags.SingleQuotedText;
```

The shorthand for these settings is `SerializationSettings.JSON5` and `SerializationSettings.JSON5PrettyPrint`.

### JSON to C# and back
The readers and writers have API for serializing/deserializing `IJSONObject` values.
If the JSON object contains text values, then a `ReadOnlySpan<char>` will be expected
to be stored inside the value type. Using types like `string` and `List<char>` makes the type
not acceptable because of the unmanaged requirement, an `UnmanagedArray<char>` is used instead.

The readers and writers have API for serializing/deserializing `IJSONObject` values:
```cs
public struct Player : IJSONObject, IDisposable
{
public int hp;
public bool alive;

private UnmanagedArray<char> name;
private Text name;

public readonly Span<char> Name
{
get => name.AsSpan();
set
{
name.Resize(value.Length);
value.CopyTo(name.AsSpan());
}
set => name.CopyFrom(value);
}

public Player(int hp, bool alive, ReadOnlySpan<char> name)
Expand All @@ -97,30 +113,41 @@ public struct Player : IJSONObject, IDisposable

void IJSONObject.Read(ref JSONReader reader)
{
//should initialize itself fully
//read hp
reader.ReadToken();
hp = (int)reader.ReadNumber(out _);

//read alive
reader.ReadToken();
alive = reader.ReadBoolean(out _);
name = new(reader.ReadText(out _));

//read name
reader.ReadToken();
Span<char> nameBuffer = stackalloc char[32];
int nameLength = reader.ReadText(nameBuffer);
name = new(nameBuffer.Slice(0, nameLength));
}

void IJSONObject.Write(JSONWriter writer)
{
writer.WriteNumber(hp);
writer.WriteBoolean(alive);
writer.WriteText(name.AsSpan());
writer.WriteProperty(nameof(hp), hp);
writer.WriteProperty(nameof(alive), alive);
writer.WriteProperty(nameof(name), name.AsSpan());
}
}

byte[] jsonBytes = File.ReadAllBytes("player.json");
using JSONReader reader = new(jsonBytes);
using Player player = reader.ReadObject<Player>();
using ByteReader reader = new(jsonBytes);
JSONReader jsonReader = new(reader);
using Player player = jsonReader.ReadObject<Player>();
ReadOnlySpan<char> name = player.Name;
```

### XML

XML is supported through the `XMLNode` type, which can be created from either a byte or a char array.
Each node has a name, content, and a list of children. Attributes can be read using the indexer.
```csharp
```cs
byte[] xmlData = File.ReadAllBytes("solution.csproj");
using XMLNode project = new(xmlData);
XMLAttribute sdk = project["Sdk"];
Expand All @@ -129,4 +156,11 @@ project.TryGetFirst("PropertyGroup", out XMLNode propertyGroup);
project.TryGetFirst("TargetFramework", out XMLNode tfm);
tfm.Content = "net9.0";
File.WriteAllText("solution.csproj", project.ToString());
```
```

### Contributing and design

Although the name of the library is `serialization`, it's not to solve serialization itself.
But instead, for providing implementations of common and easy to read/edit formats very efficiently.

And despite "common" being difficult to define, contributions to this are welcome.
2 changes: 1 addition & 1 deletion source/JSON/IJSONSerializable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
public interface IJSONSerializable
{
void Read(JSONReader reader);
void Write(JSONWriter writer);
void Write(ref JSONWriter writer);
}
}
Loading