diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 3555b4a..0000000 --- a/.gitignore +++ /dev/null @@ -1,17 +0,0 @@ -.dub -docs.json -__dummy.html -docs/ -/jstruct -jstruct.so -jstruct.dylib -jstruct.dll -jstruct.a -jstruct.lib -jstruct-test-* -*.exe -*.pdb -*.o -*.obj -*.lst -libjstruct.a diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 0a04128..0000000 --- a/LICENSE +++ /dev/null @@ -1,165 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - - This version of the GNU Lesser General Public License incorporates -the terms and conditions of version 3 of the GNU General Public -License, supplemented by the additional permissions listed below. - - 0. Additional Definitions. - - As used herein, "this License" refers to version 3 of the GNU Lesser -General Public License, and the "GNU GPL" refers to version 3 of the GNU -General Public License. - - "The Library" refers to a covered work governed by this License, -other than an Application or a Combined Work as defined below. - - An "Application" is any work that makes use of an interface provided -by the Library, but which is not otherwise based on the Library. -Defining a subclass of a class defined by the Library is deemed a mode -of using an interface provided by the Library. - - A "Combined Work" is a work produced by combining or linking an -Application with the Library. The particular version of the Library -with which the Combined Work was made is also called the "Linked -Version". - - The "Minimal Corresponding Source" for a Combined Work means the -Corresponding Source for the Combined Work, excluding any source code -for portions of the Combined Work that, considered in isolation, are -based on the Application, and not on the Linked Version. - - The "Corresponding Application Code" for a Combined Work means the -object code and/or source code for the Application, including any data -and utility programs needed for reproducing the Combined Work from the -Application, but excluding the System Libraries of the Combined Work. - - 1. Exception to Section 3 of the GNU GPL. - - You may convey a covered work under sections 3 and 4 of this License -without being bound by section 3 of the GNU GPL. - - 2. Conveying Modified Versions. - - If you modify a copy of the Library, and, in your modifications, a -facility refers to a function or data to be supplied by an Application -that uses the facility (other than as an argument passed when the -facility is invoked), then you may convey a copy of the modified -version: - - a) under this License, provided that you make a good faith effort to - ensure that, in the event an Application does not supply the - function or data, the facility still operates, and performs - whatever part of its purpose remains meaningful, or - - b) under the GNU GPL, with none of the additional permissions of - this License applicable to that copy. - - 3. Object Code Incorporating Material from Library Header Files. - - The object code form of an Application may incorporate material from -a header file that is part of the Library. You may convey such object -code under terms of your choice, provided that, if the incorporated -material is not limited to numerical parameters, data structure -layouts and accessors, or small macros, inline functions and templates -(ten or fewer lines in length), you do both of the following: - - a) Give prominent notice with each copy of the object code that the - Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the object code with a copy of the GNU GPL and this license - document. - - 4. Combined Works. - - You may convey a Combined Work under terms of your choice that, -taken together, effectively do not restrict modification of the -portions of the Library contained in the Combined Work and reverse -engineering for debugging such modifications, if you also do each of -the following: - - a) Give prominent notice with each copy of the Combined Work that - the Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the Combined Work with a copy of the GNU GPL and this license - document. - - c) For a Combined Work that displays copyright notices during - execution, include the copyright notice for the Library among - these notices, as well as a reference directing the user to the - copies of the GNU GPL and this license document. - - d) Do one of the following: - - 0) Convey the Minimal Corresponding Source under the terms of this - License, and the Corresponding Application Code in a form - suitable for, and under terms that permit, the user to - recombine or relink the Application with a modified version of - the Linked Version to produce a modified Combined Work, in the - manner specified by section 6 of the GNU GPL for conveying - Corresponding Source. - - 1) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (a) uses at run time - a copy of the Library already present on the user's computer - system, and (b) will operate properly with a modified version - of the Library that is interface-compatible with the Linked - Version. - - e) Provide Installation Information, but only if you would otherwise - be required to provide such information under section 6 of the - GNU GPL, and only to the extent that such information is - necessary to install and execute a modified version of the - Combined Work produced by recombining or relinking the - Application with a modified version of the Linked Version. (If - you use option 4d0, the Installation Information must accompany - the Minimal Corresponding Source and Corresponding Application - Code. If you use option 4d1, you must provide the Installation - Information in the manner specified by section 6 of the GNU GPL - for conveying Corresponding Source.) - - 5. Combined Libraries. - - You may place library facilities that are a work based on the -Library side by side in a single library together with other library -facilities that are not Applications and are not covered by this -License, and convey such a combined library under terms of your -choice, if you do both of the following: - - a) Accompany the combined library with a copy of the same work based - on the Library, uncombined with any other library facilities, - conveyed under the terms of this License. - - b) Give prominent notice with the combined library that part of it - is a work based on the Library, and explaining where to find the - accompanying uncombined form of the same work. - - 6. Revised Versions of the GNU Lesser General Public License. - - The Free Software Foundation may publish revised and/or new versions -of the GNU Lesser General Public License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - - Each version is given a distinguishing version number. If the -Library as you received it specifies that a certain numbered version -of the GNU Lesser General Public License "or any later version" -applies to it, you have the option of following the terms and -conditions either of that published version or of any later version -published by the Free Software Foundation. If the Library as you -received it does not specify a version number of the GNU Lesser -General Public License, you may choose any version of the GNU Lesser -General Public License ever published by the Free Software Foundation. - - If the Library as you received it specifies that a proxy can decide -whether future versions of the GNU Lesser General Public License shall -apply, that proxy's public statement of acceptance of any version is -permanent authorization for you to choose that version for the -Library. diff --git a/README.md b/README.md deleted file mode 100644 index 0af37a7..0000000 --- a/README.md +++ /dev/null @@ -1,137 +0,0 @@ -![](branding/logo.png) - -jstruct -======= - -#### _Struct JSON serializer/deserializer_ - ----- - -## 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 \ No newline at end of file diff --git a/branding/logo.xcf b/branding/logo.xcf deleted file mode 100644 index fc12cda..0000000 Binary files a/branding/logo.xcf and /dev/null differ diff --git a/docs/deserialization.md b/docs/deserialization.md new file mode 100644 index 0000000..1275936 --- /dev/null +++ b/docs/deserialization.md @@ -0,0 +1,56 @@ +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}) +``` \ No newline at end of file diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..5f31324 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,31 @@ +jstruct +======= + +#### _Struct JSON serializer/deserializer_ + +---- + +## API + +For full API documentation see [DUB API Spec](https://jstruct.dpldocs.info/index.html). + +## 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 \ No newline at end of file diff --git a/branding/logo.png b/docs/logo.png similarity index 100% rename from branding/logo.png rename to docs/logo.png diff --git a/docs/serialization.md b/docs/serialization.md new file mode 100644 index 0000000..8ef2326 --- /dev/null +++ b/docs/serialization.md @@ -0,0 +1,50 @@ +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\"]" +} +``` \ No newline at end of file diff --git a/dub.json b/dub.json deleted file mode 100644 index 561e6e1..0000000 --- a/dub.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "authors": [ - "Tristan B. Velloza Kildaire" - ], - "copyright": "Copyright © 2023, Tristan B. Velloza Kildaire", - "description": "Struct JSON serializer/deserializer", - "license": "LGPL v3.0", - "name": "jstruct", - "targetType": "library" -} \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..e1cbdda --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,6 @@ +site_name: jstruct + +theme: alabaster + +extra: + logo: logo.png \ No newline at end of file diff --git a/source/jstruct/deserializer.d b/source/jstruct/deserializer.d deleted file mode 100644 index cef2962..0000000 --- a/source/jstruct/deserializer.d +++ /dev/null @@ -1,205 +0,0 @@ -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); - } - -} \ No newline at end of file diff --git a/source/jstruct/exceptions.d b/source/jstruct/exceptions.d deleted file mode 100644 index 5bb99e7..0000000 --- a/source/jstruct/exceptions.d +++ /dev/null @@ -1,17 +0,0 @@ -module jstruct.exceptions; - -public abstract class JStructException : Exception -{ - this(string msg) - { - super("JStructException: "~msg); - } -} - -public final class SerializationError : JStructException -{ - this() - { - super("Errro serializing"); - } -} \ No newline at end of file diff --git a/source/jstruct/package.d b/source/jstruct/package.d deleted file mode 100644 index 242c64a..0000000 --- a/source/jstruct/package.d +++ /dev/null @@ -1,5 +0,0 @@ -module jstruct; - -public import jstruct.serializer; -public import jstruct.deserializer; -public import jstruct.exceptions; \ No newline at end of file diff --git a/source/jstruct/serializer.d b/source/jstruct/serializer.d deleted file mode 100644 index 15d7ca6..0000000 --- a/source/jstruct/serializer.d +++ /dev/null @@ -1,109 +0,0 @@ -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()); - } -}