Initial release

This commit is contained in:
Tristan B. Velloza Kildaire 2023-01-09 10:59:46 +02:00
parent 7f8f445c7a
commit 4da5ca4f3c
4 changed files with 455 additions and 1 deletions

129
README.md
View File

@ -7,4 +7,131 @@ jstruct
----
TODO: Add here
## Usage
### Serialization
Below we define a struct called `Person`:
```d
struct Person
{
public string firstname, lastname;
public int age;
public string[] list;
public JSONValue extraJSON;
}
```
Let's create a instance and set some values before we continue:
```d
Person p1;
p1.firstname = "Tristan";
p1.lastname = "Kildaire";
p1.age = 23;
p1.list = ["1", "2", "3"];
p1.extraJSON = parseJSON(`{"item":1, "items":[1,2,3]}`);
```
Now, we make a call to `serializeRecord` as follows:
```d
JSONValue serialized = serializeRecord(p1);
```
This returns the following JSON:
```json
{
"age": 23,
"extraJSON": {
"item": 1,
"items": [
1,
2,
3
]
},
"firstname": "Tristan",
"lastname": "Kildaire",
"list": "[\"1\", \"2\", \"3\"]"
}
```
### Deserialization
Deserialization works by having your predefined struct type and then looking up those field names in the provided JSON. Therefore for this we will be using the following struct type:
```d
struct Person
{
public string firstname, lastname;
public int age;
public bool isMale;
public JSONValue obj;
}
```
Now, let's say we were given the following JSON:
```json
{
"firstname" : "Tristan",
"lastname": "Kildaire",
"age": 23,
"obj" : {"bruh":1},
"isMale": true,
}
```
We can then deserialize the JSON to our type `Person`, with the `fromJSON` method:
```d
// Define our JSON
JSONValue json = parseJSON(`{
"firstname" : "Tristan",
"lastname": "Kildaire",
"age": 23,
"obj" : {"bruh":1},
"isMale": true,
"list": [1,2,3]
}
`);
// Deserialize
Person person = fromJSON!(Person)(json);
```
And we can confirm this with:
```d
writeln(person):
```
Which will output:
```
Person("Tristan", "Kildaire", 23, true, {"bruh":1})
```
## Installing
In order to use jstruct in your project simply run:
```bash
dub add jstruct
```
And then in your D program import as follows:
```d
import jstruct;
```
## Help wanted
There are some outstanding issues I want to be able to fix/have implemented, namely:
- [ ] Support for array serialization/deserialization - see issue #1
- [ ] Support for custom types serialization/deserialization (think `enums` for example) - see issue #2

View File

@ -1,2 +1,205 @@
module jstruct.deserializer;
import std.json;
import jstruct.exceptions : SerializationError;
import std.traits : FieldTypeTuple, FieldNameTuple;
/**
* Deserializes the provided JSON into a struct of type RecordType
*
* Params:
* jsonIn = the JSON to deserialize
* Throws:
* RemoteFieldMissing = if the field names in the provided RecordType
* cannot be found within the prpvided JSONValue `jsonIn`.
* Returns: an instance of RecordType
*/
public RecordType fromJSON(RecordType)(JSONValue jsonIn)
{
RecordType record;
// Alias as to only expand later when used in compile-time
alias structTypes = FieldTypeTuple!(RecordType);
alias structNames = FieldNameTuple!(RecordType);
alias structValues = record.tupleof;
static foreach(cnt; 0..structTypes.length)
{
debug(dbg)
{
pragma(msg, structTypes[cnt]);
pragma(msg, structNames[cnt]);
// pragma(msg, structValues[cnt]);
}
debug(dbg)
{
pragma(msg, "Bruh type");
pragma(msg, structTypes[cnt]);
// pragma(msg, __traits(identifier, mixin(structTypes[cnt])));
}
try
{
static if(__traits(isSame, mixin(structTypes[cnt]), byte))
{
mixin("record."~structNames[cnt]) = cast(byte)jsonIn[structNames[cnt]].integer();
}
else static if(__traits(isSame, mixin(structTypes[cnt]), ubyte))
{
mixin("record."~structNames[cnt]) = cast(ubyte)jsonIn[structNames[cnt]].uinteger();
}
else static if(__traits(isSame, mixin(structTypes[cnt]), short))
{
mixin("record."~structNames[cnt]) = cast(short)jsonIn[structNames[cnt]].integer();
}
else static if(__traits(isSame, mixin(structTypes[cnt]), ushort))
{
mixin("record."~structNames[cnt]) = cast(ushort)jsonIn[structNames[cnt]].uinteger();
}
else static if(__traits(isSame, mixin(structTypes[cnt]), int))
{
mixin("record."~structNames[cnt]) = cast(int)jsonIn[structNames[cnt]].integer();
}
else static if(__traits(isSame, mixin(structTypes[cnt]), uint))
{
mixin("record."~structNames[cnt]) = cast(uint)jsonIn[structNames[cnt]].uinteger();
}
else static if(__traits(isSame, mixin(structTypes[cnt]), ulong))
{
mixin("record."~structNames[cnt]) = cast(ulong)jsonIn[structNames[cnt]].uinteger();
}
else static if(__traits(isSame, mixin(structTypes[cnt]), long))
{
mixin("record."~structNames[cnt]) = cast(long)jsonIn[structNames[cnt]].integer();
}
else static if(__traits(isSame, mixin(structTypes[cnt]), string))
{
mixin("record."~structNames[cnt]) = jsonIn[structNames[cnt]].str();
debug(dbg)
{
pragma(msg,"record."~structNames[cnt]);
}
}
else static if(__traits(isSame, mixin(structTypes[cnt]), JSONValue))
{
mixin("record."~structNames[cnt]) = jsonIn[structNames[cnt]];
debug(dbg)
{
pragma(msg,"record."~structNames[cnt]);
}
}
else static if(__traits(isSame, mixin(structTypes[cnt]), bool))
{
mixin("record."~structNames[cnt]) = jsonIn[structNames[cnt]].boolean();
debug(dbg)
{
pragma(msg,"record."~structNames[cnt]);
}
}
//FIXME: Not sure how to get array support going, very new to meta programming
else static if(__traits(isSame, mixin(structTypes[cnt]), mixin(structTypes[cnt])[]))
{
mixin("record."~structNames[cnt]) = jsonIn[structNames[cnt]].boolean();
debug(dbg)
{
pragma(msg,"record."~structNames[cnt]);
}
}
else
{
// throw new
//TODO: Throw error
debug(dbg)
{
pragma(msg, "Unknown type for de-serialization");
}
}
}
catch(JSONException e)
{
throw new SerializationError();
}
}
return record;
}
unittest
{
import std.string : cmp;
import std.stdio : writeln;
struct Person
{
public string firstname, lastname;
public int age;
public bool isMale;
public JSONValue obj;
public int[] list;
}
JSONValue json = parseJSON(`{
"firstname" : "Tristan",
"lastname": "Kildaire",
"age": 23,
"obj" : {"bruh":1},
"isMale": true,
"list": [1,2,3]
}
`);
Person person = fromJSON!(Person)(json);
debug(dbg)
{
writeln(person);
}
assert(cmp(person.firstname, "Tristan") == 0);
assert(cmp(person.lastname, "Kildaire") == 0);
assert(person.age == 23);
assert(person.isMale == true);
assert(person.obj["bruh"].integer() == 1);
//TODO: list test case
}
unittest
{
import std.string : cmp;
import std.stdio : writeln;
struct Person
{
public string firstname, lastname;
public int age;
public bool isMale;
public JSONValue obj;
public int[] list;
}
JSONValue json = parseJSON(`{
"firstname" : "Tristan",
"lastname": "Kildaire",
"age": 23,
"obj" : {"bruh":1},
"list": [1,2,3]
}
`);
try
{
Person person = fromJSON!(Person)(json);
assert(false);
}
catch(SerializationError)
{
assert(true);
}
}

17
source/exceptions.d Normal file
View File

@ -0,0 +1,17 @@
module jstruct.exceptions;
public abstract class JStructException : Exception
{
this(string msg)
{
super("JStructException: "~msg);
}
}
public final class SerializationError : JStructException
{
this()
{
super("Errro serializing");
}
}

View File

@ -1,2 +1,109 @@
module jstruct.serializer;
import std.json;
import std.conv : to;
import std.traits : FieldTypeTuple, FieldNameTuple;
public JSONValue serializeRecord(RecordType)(RecordType record)
{
// Final JSON to submit
JSONValue builtJSON;
// Alias as to only expand later when used in compile-time
alias structTypes = FieldTypeTuple!(RecordType);
alias structNames = FieldNameTuple!(RecordType);
alias structValues = record.tupleof;
static foreach(cnt; 0..structTypes.length)
{
debug(dbg)
{
pragma(msg, structTypes[cnt]);
pragma(msg, structNames[cnt]);
// pragma(msg, structValues[cnt]);
}
static if(__traits(isSame, mixin(structTypes[cnt]), int))
{
builtJSON[structNames[cnt]] = structValues[cnt];
}
else static if(__traits(isSame, mixin(structTypes[cnt]), uint))
{
builtJSON[structNames[cnt]] = structValues[cnt];
}
else static if(__traits(isSame, mixin(structTypes[cnt]), ulong))
{
builtJSON[structNames[cnt]] = structValues[cnt];
}
else static if(__traits(isSame, mixin(structTypes[cnt]), long))
{
builtJSON[structNames[cnt]] = structValues[cnt];
}
else static if(__traits(isSame, mixin(structTypes[cnt]), string))
{
builtJSON[structNames[cnt]] = structValues[cnt];
}
else static if(__traits(isSame, mixin(structTypes[cnt]), JSONValue))
{
builtJSON[structNames[cnt]] = structValues[cnt];
}
else static if(__traits(isSame, mixin(structTypes[cnt]), bool))
{
builtJSON[structNames[cnt]] = structValues[cnt];
}
else
{
debug(dbg)
{
pragma(msg, "Yaa");
}
builtJSON[structNames[cnt]] = to!(string)(structValues[cnt]);
}
}
return builtJSON;
}
// Test serialization of a struct to JSON
private enum EnumType
{
DOG,
CAT
}
unittest
{
import std.algorithm.searching : canFind;
import std.string : cmp;
import std.stdio : writeln;
struct Person
{
public string firstname, lastname;
public int age;
public string[] list;
public JSONValue extraJSON;
public EnumType eType;
}
Person p1;
p1.firstname = "Tristan";
p1.lastname = "Kildaire";
p1.age = 23;
p1.list = ["1", "2", "3"];
p1.extraJSON = parseJSON(`{"item":1, "items":[1,2,3]}`);
p1.eType = EnumType.CAT;
JSONValue serialized = serializeRecord(p1);
string[] keys = serialized.object().keys();
assert(canFind(keys, "firstname") && cmp(serialized["firstname"].str(), "Tristan") == 0);
assert(canFind(keys, "lastname") && cmp(serialized["lastname"].str(), "Kildaire") == 0);
assert(canFind(keys, "age") && serialized["age"].integer() == 23);
debug(dbg)
{
writeln(serialized.toPrettyString());
}
}