Comparing version
@@ -1,4 +0,4 @@ | ||
import { JSON, parseBooleanArray, parseMap, parseNumberArray } from ".."; | ||
import { JSON } from ".."; | ||
@json | ||
class Vector { | ||
class Vec2 { | ||
x: f32; | ||
@@ -8,3 +8,3 @@ y: f32; | ||
const vec: Vector = blackbox<Vector>({ | ||
const vec: Vec2 = blackbox<Vec2>({ | ||
x: 0.0, | ||
@@ -30,3 +30,3 @@ y: 0.0, | ||
bench("Stringify Vector", () => { | ||
bench("Stringify Object (Vec2)", () => { | ||
blackbox(JSON.stringify(vec)); | ||
@@ -55,4 +55,4 @@ }); | ||
bench("Parse Vector", () => { | ||
blackbox(parseMap<Map<string, f32>>(blackbox('{"x":0.0,"y":0.0}'))); | ||
bench("Parse Object (Vec2)", () => { | ||
blackbox(JSON.parse<Vec2>(blackbox('{"x":0.0,"y":0.0}'))); | ||
}); | ||
@@ -62,3 +62,3 @@ | ||
blackbox( | ||
parseBooleanArray<boolean[]>(blackbox("[true,false,true,false,true]")) | ||
JSON.parse<boolean[]>(blackbox("[true,false,true,false,true]")) | ||
); | ||
@@ -68,7 +68,7 @@ }); | ||
bench("Parse Integer Array", () => { | ||
blackbox(parseNumberArray<u32[]>(blackbox("[1,2,3,4,5]"))); | ||
blackbox(JSON.parse<u32[]>(blackbox("[1,2,3,4,5]"))); | ||
}); | ||
bench("Parse Float Array", () => { | ||
blackbox(parseNumberArray<f32[]>(blackbox("[1.0,2.0,3.0,4.0,5.0]"))); | ||
blackbox(JSON.parse<f32[]>(blackbox("[1.0,2.0,3.0,4.0,5.0]"))); | ||
}); |
export const commaCode = ",".charCodeAt(0); | ||
export const quoteCode = "".charCodeAt(0); | ||
export const quoteCode = "\"".charCodeAt(0); | ||
export const backSlashCode = "\\".charCodeAt(0); | ||
@@ -4,0 +4,0 @@ export const leftBraceCode = "{".charCodeAt(0); |
@@ -29,13 +29,9 @@ import { StringSink } from "as-string-sink/assembly"; | ||
// String | ||
if (isString(data)) { | ||
return serializeString(<string>data); | ||
if (isString<T>()) { | ||
return '"' + (<string>data).replaceAll('"', '\\"') + '"'; | ||
} | ||
// Boolean | ||
else if (isBoolean(data)) { | ||
else if (isBoolean<T>()) { | ||
return data ? "true" : "false"; | ||
} | ||
// Null | ||
else if (isNullable<T>() && data == null) { | ||
return "null"; | ||
} | ||
// Integers/Floats | ||
@@ -50,16 +46,24 @@ // @ts-ignore | ||
else if (isDefined(data.__JSON_Serialize)) { | ||
//@ts-ignore | ||
// @ts-ignore | ||
return data.__JSON_Serialize(); | ||
} | ||
// ArrayLike | ||
else if (isArrayLike(data)) { | ||
else if (isArrayLike<T>()) { | ||
let result = new StringSink("["); | ||
// @ts-ignore | ||
if (data.length == 0) return "[]"; | ||
// @ts-ignore | ||
for (let i = 0; i < data.length - 1; i++) { | ||
// @ts-ignore | ||
result.write(stringify(unchecked(data[i]))); | ||
result.write(","); | ||
} | ||
// @ts-ignore | ||
result.write(stringify(unchecked(data[data.length - 1]))); | ||
result.write("]"); | ||
return result.toString(); | ||
} | ||
// Null | ||
else if (isNullable<T>() && data == null) { | ||
return "null"; | ||
} else { | ||
@@ -96,3 +100,3 @@ return "null"; | ||
let key: string = '' | ||
let instr: u32 = 0 | ||
let instr: boolean = false | ||
let char: u32 = 0 | ||
@@ -102,5 +106,6 @@ let depth: u32 = 0 | ||
for (let i: u32 = 1; i < len; i++) { | ||
char = data.charCodeAt(i) | ||
if (char === "\"".charCodeAt(0) && data.charCodeAt(i - 1) !== "/".charCodeAt(0)) instr = (instr ? 0 : 1) | ||
else if (instr === 0) { | ||
char = data.charCodeAt(i); | ||
if (instr === false && char === quoteCode) instr = true; | ||
else if (instr === true && char === quoteCode && data.charCodeAt(i - 1) !== "/".charCodeAt(0)) instr = false; | ||
if (instr === false) { | ||
if (char === leftBraceCode || char === leftBracketCode) depth++ | ||
@@ -110,2 +115,3 @@ if (char === rightBraceCode || char === rightBracketCode) fdepth++ | ||
if (depth !== 0 && depth === fdepth) { | ||
//console.log(`Found Struct: ${data.slice(lastPos + 1, i + 1)}`) | ||
result.set(key, data.slice(lastPos + 1, i + 1)) | ||
@@ -118,8 +124,9 @@ // Reset the depth | ||
} | ||
if (depth === 0) { | ||
if (!instr && depth === 0) { | ||
if (char === colonCode) { | ||
key = data.slice(lastPos + 1, i - 1) | ||
//console.log(`Found Key: ${data.slice(lastPos + 1, i - 1)}`) | ||
lastPos = i | ||
} | ||
else if (char === commaCode) { | ||
} else if (char === commaCode) { | ||
//console.log(`Found Comma: ${data.slice(lastPos + 1, i)}`) | ||
if ((i - lastPos) > 0) result.set(key, data.slice(lastPos + 1, i)) | ||
@@ -131,3 +138,5 @@ lastPos = i + 1 | ||
if ((len - lastPos) > 0) result.set(key, data.slice(lastPos + 1, len)) | ||
if ((len - lastPos) > 1 && (len - lastPos) !== 0) { | ||
result.set(key, data.slice(lastPos + 1, len)) | ||
} | ||
// @ts-ignore | ||
@@ -142,9 +151,5 @@ return schema.__JSON_Deserialize(result) | ||
// @ts-ignore | ||
@inline | ||
function serializeString(data: string): string { | ||
return '"' + data.replaceAll('"', '\\"') + '"'; | ||
} | ||
// @ts-ignore | ||
@inline | ||
function parseString(data: string): string { | ||
@@ -312,35 +317,2 @@ return data.slice(1, data.length - 1).replaceAll('\\"', '"'); | ||
export function parseObject(data: string): void { | ||
//const obj = instantiate<T>() | ||
const result = new Array<string>(); | ||
let lastPos: u32 = 0; | ||
let char: u32 = 0; | ||
let i: u32 = 1; | ||
let key: string = ""; | ||
let isKey: boolean = false; | ||
for (; i < u32(data.length - 1); i++) { | ||
char = data.charCodeAt(i); | ||
if (isKey == false) { | ||
if (char == colonCode) { | ||
key = data.slice(lastPos + 2, i - 1); | ||
//console.log(`Found Key: ${key}`); | ||
result.push(key); | ||
lastPos = ++i; | ||
isKey = true; | ||
} | ||
} else { | ||
if (char == commaCode) { | ||
const val = data.slice(lastPos, i); | ||
lastPos = i; | ||
isKey = false; | ||
//console.log(`Found Val: ${val}`) | ||
result.push(val); | ||
} | ||
} | ||
} | ||
result.push(data.slice(lastPos, data.length - 1)); | ||
//console.log(stringify(result)) | ||
//return obj | ||
} | ||
class Nullable { } |
import "wasi"; | ||
import { JSON } from "."; | ||
// @ts-ignore | ||
@json | ||
@@ -9,2 +10,4 @@ class Vec2 { | ||
} | ||
// @ts-ignore | ||
@json | ||
@@ -31,7 +34,26 @@ class Player { | ||
const stringified = JSON.stringify<Player>(data); | ||
// '{"firstName":"Emmet","lastName":"West","lastActive":[8,27,2022],"age":23}' | ||
// { | ||
// "firstName": "Emmet", | ||
// "lastName": "West", | ||
// "lastActive": [8, 27, 2022], | ||
// "age": 23, | ||
// "pos": { | ||
// "x": -3.4000000953674318, | ||
// "y": 1.2000000476837159 | ||
// } | ||
// } | ||
console.log(`Stringified: ${stringified}`); | ||
const parsed = JSON.parse<Player>(stringified) | ||
// { firstName: "Emmet", lastName: "West", "lastActive": [8,27,2022], age: 23 } | ||
console.log(`Parsed: ${JSON.stringify(parsed)}`) | ||
data.age = 16 | ||
console.log(`Stringified2: ${JSON.stringify<Player>(data)}`); | ||
const parsed = JSON.parse<Player>(stringified); | ||
// Player { | ||
// firstName: "Emmet", | ||
// lastName: "West", | ||
// lastActive: [8, 27, 2022], | ||
// age: 23, | ||
// pos: { | ||
// x: -3.4000000953674318, | ||
// y: 1.2000000476837159 | ||
// } | ||
// } | ||
console.log(`Parsed: ${JSON.stringify(parsed)}`); |
{ | ||
"name": "json-as", | ||
"version": "0.4.2", | ||
"version": "0.4.3", | ||
"description": "JSON encoder/decoder for AssemblyScript", | ||
@@ -20,2 +20,3 @@ "types": "assembly/index.ts", | ||
"devDependencies": { | ||
"@as-pect/cli": "^7.0.7", | ||
"@as-tral/cli": "^1.1.1", | ||
@@ -25,4 +26,2 @@ "as-console": "^6.0.2", | ||
"assemblyscript-prettier": "^1.0.2", | ||
"benchmark": "^2.1.4", | ||
"microtime": "^3.0.0", | ||
"typescript": "^4.7.2" | ||
@@ -32,3 +31,3 @@ }, | ||
"as-string-sink": "^0.5.0", | ||
"as-variant": "^0.3.0" | ||
"as-variant": "^0.4.0" | ||
}, | ||
@@ -44,3 +43,4 @@ "repository": { | ||
"deserialize", | ||
"dynamic" | ||
"dynamic", | ||
"serde" | ||
], | ||
@@ -47,0 +47,0 @@ "bugs": { |
@@ -70,12 +70,42 @@ # AS-JSON | ||
const stringified = JSON.stringify<Player>(data); | ||
// '{"firstName":"Emmet","lastName":"West","lastActive":[8,27,2022],"age":23}' | ||
// { | ||
// "firstName": "Emmet", | ||
// "lastName": "West", | ||
// "lastActive": [8, 27, 2022], | ||
// "age": 23, | ||
// "pos": { | ||
// "x": -3.4000000953674318, | ||
// "y": 1.2000000476837159 | ||
// } | ||
// } | ||
console.log(`Stringified: ${stringified}`); | ||
const parsed = JSON.parse<Player>(stringified) | ||
// { firstName: "Emmet", lastName: "West", "lastActive": [8,27,2022], age: 23 } | ||
console.log(`Parsed: ${JSON.stringify(parsed)}`) | ||
const parsed = JSON.parse<Player>(stringified); | ||
// Player { | ||
// firstName: "Emmet", | ||
// lastName: "West", | ||
// lastActive: [8, 27, 2022], | ||
// age: 23, | ||
// pos: { | ||
// x: -3.4000000953674318, | ||
// y: 1.2000000476837159 | ||
// } | ||
// } | ||
console.log(`Parsed: ${JSON.stringify(parsed)}`); | ||
``` | ||
## FAQ | ||
- Does it support the full JSON spec? | ||
Yes, as-json supports the full JSON specification and trys to mimic the JSON API as much as possible | ||
- What are the differences between as-json and the JSON API? | ||
The main difference between as-json and the JSON API is the ability to create new fields in objects during runtime. Since classes are static, Map ser/de support will be added soon allowing the user to parse and stringify dynamic objects and their properties | ||
- Does this support nested structures? | ||
Yes, as-json supports nested structures | ||
- Does this support whitespace? | ||
No, as-json does not support whitespace yet. That will come once we find a performant way to work around whitespace. | ||
- How fast is it? | ||
Really fast. For example, here are some benchmarks for ser/de a Vec2 with as-json | ||
## Issues | ||
Please submit an issue to https://github.com/JairusSW/as-json/issues if you find anything wrong with this library |
@@ -8,4 +8,4 @@ import { ClassDecorator, registerDecorator } from "visitor-as/dist/decorator.js"; | ||
this.sources = []; | ||
this.encodeStmts = new Map(); | ||
this.decodeCode = new Map(); | ||
this.encodeStmts = []; | ||
this.decodeStmts = []; | ||
} | ||
@@ -19,11 +19,6 @@ visitMethodDeclaration(node) { } | ||
const type = getName(node.type); | ||
const className = this.currentClass.name.text; | ||
if (!this.encodeStmts.has(className)) | ||
this.encodeStmts.set(className, []); | ||
if (!this.decodeCode.has(className)) | ||
this.decodeCode.set(className, []); | ||
// @ts-ignore | ||
this.encodeStmts.get(className).push(`this.__JSON_Serialized += '' + '"' + '${name}' + '"' + ':' + JSON.stringify<${type}>(this.${name}) + ',';`); | ||
this.encodeStmts.push(`"${name}":\${JSON.stringify<${type}>(this.${name})},`); | ||
// @ts-ignore | ||
this.decodeCode.get(className).push(`${name}: JSON.parse<${type}>(values.get("${name}")),\n`); | ||
this.decodeStmts.push(`${name}: JSON.parse<${type}>(values.get("${name}")),\n`); | ||
} | ||
@@ -36,16 +31,12 @@ visitClassDeclaration(node) { | ||
const name = getName(node); | ||
this.encodeStmts.delete(name); | ||
this.decodeCode.delete(name); | ||
this.visit(node.members); | ||
const serializedProp = `__JSON_Serialized: string = "";`; | ||
let serializeFunc = ``; | ||
if (this.encodeStmts.has(name) && this.encodeStmts.get(name)) { | ||
if (this.encodeStmts.length > 0) { | ||
const stmt = this.encodeStmts[this.encodeStmts.length - 1]; | ||
this.encodeStmts[this.encodeStmts.length - 1] = stmt.slice(0, stmt.length - 1); | ||
serializeFunc = ` | ||
@inline | ||
__JSON_Serialize(): string { | ||
if (!this.__JSON_Serialized) { | ||
${ // @ts-ignore | ||
this.encodeStmts.get(name).join("\n")}; | ||
this.__JSON_Serialized = "{" + this.__JSON_Serialized.slice(0, this.__JSON_Serialized.length - 1) + "}"; | ||
} | ||
return this.__JSON_Serialized; | ||
return \`{${this.encodeStmts.join("")}}\`; | ||
} | ||
@@ -56,2 +47,3 @@ `; | ||
serializeFunc = ` | ||
@inline | ||
__JSON_Serialize(): string { | ||
@@ -63,10 +55,13 @@ return "{}"; | ||
const deserializeFunc = ` | ||
@inline | ||
__JSON_Deserialize(values: Map<string, string>): ${name} { | ||
return { | ||
${ // @ts-ignore | ||
this.decodeCode.get(name) ? this.decodeCode.get(name).join("") : ""} | ||
this.decodeStmts.join("")} | ||
} | ||
} | ||
`; | ||
//console.log(serializedProp, serializeFunc, deserializeFunc) | ||
this.encodeStmts = []; | ||
this.decodeStmts = []; | ||
console.log(serializeFunc, deserializeFunc); | ||
const serializedProperty = SimpleParser.parseClassMember(serializedProp, node); | ||
@@ -73,0 +68,0 @@ node.members.push(serializedProperty); |
{ | ||
"name": "@json-as/transform", | ||
"version": "0.4.2", | ||
"version": "0.4.3", | ||
"description": "JSON encoder/decoder for AssemblyScript", | ||
@@ -5,0 +5,0 @@ "main": "./lib/index.js", |
@@ -14,4 +14,4 @@ import { | ||
public sources: Source[] = []; | ||
public encodeStmts = new Map<string, string[]>(); | ||
public decodeCode = new Map<string, string[]>(); | ||
public encodeStmts: string[] = []; | ||
public decodeStmts: string[] = []; | ||
@@ -27,12 +27,9 @@ visitMethodDeclaration(node: MethodDeclaration): void {} | ||
const className = this.currentClass!.name.text | ||
if (!this.encodeStmts.has(className)) this.encodeStmts.set(className, []) | ||
if (!this.decodeCode.has(className)) this.decodeCode.set(className, []) | ||
// @ts-ignore | ||
this.encodeStmts.get(className).push( | ||
`this.__JSON_Serialized += '' + '"' + '${name}' + '"' + ':' + JSON.stringify<${type}>(this.${name}) + ',';` | ||
this.encodeStmts.push( | ||
`"${name}":\${JSON.stringify<${type}>(this.${name})},` | ||
); | ||
// @ts-ignore | ||
this.decodeCode.get(className).push( | ||
this.decodeStmts.push( | ||
`${name}: JSON.parse<${type}>(values.get("${name}")),\n` | ||
@@ -50,4 +47,2 @@ ); | ||
this.encodeStmts.delete(name); | ||
this.decodeCode.delete(name); | ||
this.visit(node.members); | ||
@@ -59,11 +54,9 @@ | ||
if (this.encodeStmts.has(name) && this.encodeStmts.get(name)) { | ||
if (this.encodeStmts.length > 0) { | ||
const stmt = this.encodeStmts[this.encodeStmts.length - 1]! | ||
this.encodeStmts[this.encodeStmts.length - 1] = stmt!.slice(0, stmt.length - 1) | ||
serializeFunc = ` | ||
@inline | ||
__JSON_Serialize(): string { | ||
if (!this.__JSON_Serialized) { | ||
${// @ts-ignore | ||
this.encodeStmts.get(name).join("\n")}; | ||
this.__JSON_Serialized = "{" + this.__JSON_Serialized.slice(0, this.__JSON_Serialized.length - 1) + "}"; | ||
} | ||
return this.__JSON_Serialized; | ||
return \`{${this.encodeStmts.join("")}}\`; | ||
} | ||
@@ -73,2 +66,3 @@ ` | ||
serializeFunc = ` | ||
@inline | ||
__JSON_Serialize(): string { | ||
@@ -81,10 +75,13 @@ return "{}"; | ||
const deserializeFunc = ` | ||
@inline | ||
__JSON_Deserialize(values: Map<string, string>): ${name} { | ||
return { | ||
${// @ts-ignore | ||
this.decodeCode.get(name) ? this.decodeCode.get(name).join("") : ""} | ||
this.decodeStmts.join("")} | ||
} | ||
} | ||
`; | ||
//console.log(serializedProp, serializeFunc, deserializeFunc) | ||
this.encodeStmts = []; | ||
this.decodeStmts = []; | ||
console.log(serializeFunc, deserializeFunc) | ||
const serializedProperty = SimpleParser.parseClassMember(serializedProp, node); | ||
@@ -91,0 +88,0 @@ node.members.push(serializedProperty); |
@@ -17,3 +17,3 @@ { | ||
// "outFile": "./", /* Concatenate and emit output to single file. */ | ||
"outDir": "./lib/" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */, | ||
"outDir": "./lib" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */, | ||
// "composite": true, /* Enable project compilation */ | ||
@@ -20,0 +20,0 @@ // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ |
48024
7.21%6
-14.29%23
27.78%1222
7.76%110
37.5%+ Added
- Removed
- Removed
Updated