2022-06-09 19:54:43 +02:00
|
|
|
import * as plugins from './smartjson.plugins.js';
|
2024-03-03 10:29:18 +01:00
|
|
|
import * as bufferhandling from './bufferhandling.js';
|
2019-02-14 23:16:34 +01:00
|
|
|
|
2024-04-17 20:10:59 +02:00
|
|
|
interface JsonObject {
|
|
|
|
[key: string]: any;
|
|
|
|
}
|
|
|
|
|
2020-10-05 11:22:16 +00:00
|
|
|
/**
|
|
|
|
* allows you to parse a json
|
|
|
|
*/
|
2024-03-03 10:29:18 +01:00
|
|
|
export const parse = bufferhandling.parse;
|
2019-02-14 23:16:34 +01:00
|
|
|
|
2024-04-17 20:10:59 +02:00
|
|
|
export const parseJsonL = (jsonlData: string): JsonObject[] => {
|
2025-09-12 19:16:52 +00:00
|
|
|
const lines = jsonlData.split('\n');
|
2024-04-17 20:10:59 +02:00
|
|
|
const parsedData: JsonObject[] = lines.reduce((acc, line) => {
|
2025-09-12 19:16:52 +00:00
|
|
|
const trimmed = line.trim();
|
|
|
|
if (trimmed.length > 0) {
|
|
|
|
acc.push(parse(trimmed));
|
2024-04-17 20:10:59 +02:00
|
|
|
}
|
|
|
|
return acc;
|
|
|
|
}, [] as JsonObject[]);
|
|
|
|
return parsedData;
|
|
|
|
}
|
|
|
|
|
2025-09-12 19:30:15 +00:00
|
|
|
export const stringifyJsonL = (items: any[]): string => {
|
|
|
|
return items.map((item) => stringify(item)).join('\n');
|
|
|
|
}
|
|
|
|
|
2020-10-05 11:22:16 +00:00
|
|
|
/**
|
2020-10-05 11:26:06 +00:00
|
|
|
*
|
2020-10-05 11:22:16 +00:00
|
|
|
* @param objArg
|
|
|
|
* @param optionsArg
|
|
|
|
*/
|
2022-06-09 19:54:43 +02:00
|
|
|
export const stringify = (
|
|
|
|
objArg: any,
|
|
|
|
simpleOrderArray?: string[],
|
|
|
|
optionsArg: plugins.IStableJsonTypes['Options'] = {}
|
|
|
|
): string => {
|
2024-03-03 10:29:18 +01:00
|
|
|
const bufferedJson = bufferhandling.stringify(objArg);
|
2020-10-05 11:22:16 +00:00
|
|
|
objArg = JSON.parse(bufferedJson);
|
2025-09-12 19:30:15 +00:00
|
|
|
// derive a simple comparator from simpleOrderArray if provided and no custom cmp supplied
|
|
|
|
let options = { ...optionsArg };
|
|
|
|
if (simpleOrderArray && !options.cmp) {
|
|
|
|
const order = new Map<string, number>();
|
|
|
|
simpleOrderArray.forEach((key, idx) => order.set(key, idx));
|
|
|
|
options.cmp = (a, b) => {
|
|
|
|
const aIdx = order.has(a.key) ? (order.get(a.key) as number) : Number.POSITIVE_INFINITY;
|
|
|
|
const bIdx = order.has(b.key) ? (order.get(b.key) as number) : Number.POSITIVE_INFINITY;
|
|
|
|
if (aIdx !== bIdx) return aIdx - bIdx;
|
|
|
|
// fallback to lexicographic order for stable behavior
|
|
|
|
return a.key < b.key ? -1 : a.key > b.key ? 1 : 0;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
let returnJson = plugins.stableJson(objArg, options);
|
2022-06-09 19:54:43 +02:00
|
|
|
return returnJson;
|
2020-10-05 11:22:16 +00:00
|
|
|
};
|
|
|
|
|
2023-08-24 10:48:51 +02:00
|
|
|
export const stringifyPretty = (objectArg: any) => {
|
|
|
|
const stringified = stringify(objectArg);
|
|
|
|
const object = JSON.parse(stringified);
|
|
|
|
return JSON.stringify(object, null, 2);
|
|
|
|
}
|
|
|
|
|
2022-09-13 21:37:42 +02:00
|
|
|
export const stringifyBase64 = (...args: Parameters<typeof stringify>): string => {
|
|
|
|
const stringifiedResult = stringify(...args);
|
|
|
|
return plugins.smartstring.base64.encodeUri(stringifiedResult);
|
2022-10-26 10:56:01 +02:00
|
|
|
};
|
2022-09-13 21:37:42 +02:00
|
|
|
|
|
|
|
export const parseBase64 = (base64JsonStringArg: string) => {
|
2025-09-12 19:16:52 +00:00
|
|
|
const base64 = plugins.smartstring.base64 as any;
|
|
|
|
const decodeFn: (input: string) => string = base64.decodeUri || base64.decode;
|
|
|
|
const simpleStringified = decodeFn(base64JsonStringArg);
|
2022-09-13 21:37:42 +02:00
|
|
|
return parse(simpleStringified);
|
2022-10-26 10:56:01 +02:00
|
|
|
};
|
2022-09-13 21:37:42 +02:00
|
|
|
|
2020-10-05 11:22:16 +00:00
|
|
|
export class Smartjson {
|
2020-10-05 11:13:17 +00:00
|
|
|
/**
|
|
|
|
* enfolds data from an object
|
|
|
|
*/
|
2025-09-12 19:16:52 +00:00
|
|
|
public static enfoldFromObject<T extends typeof Smartjson>(this: T, objectArg: any): InstanceType<T> {
|
|
|
|
const newInstance = new this() as InstanceType<T>;
|
2025-09-12 19:30:15 +00:00
|
|
|
const saveables: string[] = (newInstance as any).saveableProperties || [];
|
2020-10-05 11:13:17 +00:00
|
|
|
for (const keyName in objectArg) {
|
2025-09-12 19:30:15 +00:00
|
|
|
if (saveables.indexOf(keyName) !== -1) {
|
|
|
|
(newInstance as any)[keyName] = objectArg[keyName];
|
2020-10-05 11:13:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return newInstance;
|
|
|
|
}
|
2019-02-14 23:16:34 +01:00
|
|
|
|
2020-10-05 11:22:16 +00:00
|
|
|
/**
|
|
|
|
* enfold from json
|
|
|
|
*/
|
2025-09-12 19:16:52 +00:00
|
|
|
public static enfoldFromJson<T extends typeof Smartjson>(this: T, jsonArg: string): InstanceType<T> {
|
2020-10-05 11:22:16 +00:00
|
|
|
const objectFromJson = parse(jsonArg);
|
|
|
|
return this.enfoldFromObject(objectFromJson);
|
|
|
|
}
|
|
|
|
|
2019-02-14 23:16:34 +01:00
|
|
|
// ========
|
|
|
|
// INSTANCE
|
|
|
|
// ========
|
2017-02-27 14:05:03 +01:00
|
|
|
|
2019-08-09 10:55:17 +02:00
|
|
|
public saveableProperties: string[];
|
2017-02-27 14:05:03 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* folds a class into an object
|
|
|
|
*/
|
2019-08-09 10:55:17 +02:00
|
|
|
public foldToObject() {
|
2025-09-12 19:16:52 +00:00
|
|
|
const trackSet = new Set<Smartjson>();
|
2025-09-12 19:30:15 +00:00
|
|
|
trackSet.add(this);
|
|
|
|
return this.foldToObjectInternal(trackSet);
|
2017-02-27 14:05:03 +01:00
|
|
|
}
|
2020-10-05 11:22:16 +00:00
|
|
|
|
2025-09-12 19:16:52 +00:00
|
|
|
private foldToObjectInternal(trackSet: Set<Smartjson>) {
|
|
|
|
const result: { [key: string]: any } = {};
|
|
|
|
const foldValue = (val: any): any => {
|
|
|
|
if (val instanceof Smartjson) {
|
|
|
|
if (trackSet.has(val)) {
|
|
|
|
throw new Error('cycle detected');
|
|
|
|
}
|
|
|
|
trackSet.add(val);
|
|
|
|
return val.foldToObjectInternal(trackSet);
|
|
|
|
}
|
|
|
|
if (Array.isArray(val)) {
|
|
|
|
return val.map((item) => foldValue(item));
|
|
|
|
}
|
|
|
|
return plugins.lodashCloneDeep(val);
|
|
|
|
};
|
2025-09-12 19:30:15 +00:00
|
|
|
const props: string[] = (this as any).saveableProperties || [];
|
|
|
|
for (const keyName of props) {
|
2025-09-12 19:16:52 +00:00
|
|
|
const value = this[keyName];
|
|
|
|
result[keyName] = foldValue(value);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2020-10-05 11:22:16 +00:00
|
|
|
/**
|
|
|
|
* folds a class into an object
|
|
|
|
*/
|
|
|
|
public foldToJson() {
|
|
|
|
const foldedObject = this.foldToObject();
|
2022-06-09 19:54:43 +02:00
|
|
|
return stringify(foldedObject);
|
2020-10-05 11:22:16 +00:00
|
|
|
}
|
2017-02-27 14:05:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Decorator that marks a property as foldable
|
|
|
|
*/
|
2021-01-28 02:21:23 +00:00
|
|
|
export const foldDec = () => {
|
2017-02-27 14:05:03 +01:00
|
|
|
return (target: any, key: string) => {
|
2018-07-24 00:38:54 +02:00
|
|
|
if (!target.saveableProperties) {
|
|
|
|
target.saveableProperties = [];
|
|
|
|
}
|
|
|
|
target.saveableProperties.push(key);
|
|
|
|
};
|
|
|
|
};
|
2021-01-28 02:21:23 +00:00
|
|
|
|
|
|
|
export const deepEqualObjects = (object1: any, object2: any): boolean => {
|
|
|
|
const object1String = stringify(object1);
|
|
|
|
const object2String = stringify(object2);
|
|
|
|
return object1String === object2String;
|
2022-06-09 19:54:43 +02:00
|
|
|
};
|
2024-03-19 13:30:15 +01:00
|
|
|
|
|
|
|
export const deepEqualJsonLStrings = (jsonLString1: string, jsonLString2: string): boolean => {
|
2025-09-12 19:16:52 +00:00
|
|
|
const firstArray = parseJsonL(jsonLString1);
|
|
|
|
const secondArray = parseJsonL(jsonLString2);
|
2024-03-19 13:30:15 +01:00
|
|
|
return deepEqualObjects(firstArray, secondArray);
|
|
|
|
}
|