Skip to content

Commit f4e9312

Browse files
committed
Marshalling objects to string no longer uses JSON.stringify. Rather, it tries to correctly format the object as close to native as possible.
1 parent 1c8057a commit f4e9312

File tree

3 files changed

+97
-1
lines changed

3 files changed

+97
-1
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ The marshalled version of the input data.
5252

5353
## Changelog:
5454

55+
**v1.0.7**:
56+
57+
- Marshalling objects to string no longer uses JSON.stringify. Rather, it tries to correctly format the object as close to native as possible.
58+
5559
**v1.0.6**:
5660

5761
- Added marshalling to/from `function`.

src/Marshaller.ts

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,13 +66,47 @@ export class Marshaller implements IMarshaller {
6666
return () => data;
6767
}
6868

69+
/**
70+
* Quotes the given string if needed. It will escape the string if it already starts and/or ends with a clashing quote.
71+
* @param {string} content
72+
* @returns {string}
73+
*/
74+
private quoteIfNecessary (content: string): string {
75+
if (!(typeof content === "string")) return content;
76+
const firstChar = content[0];
77+
const lastChar = content[content.length - 1];
78+
let str = "`";
79+
const startsWithClashingQuote = firstChar === "`";
80+
const endsWithClashingQuote = lastChar === "`";
81+
const startOffset = startsWithClashingQuote ? 1 : 0;
82+
const endOffset = endsWithClashingQuote ? 1 : 0;
83+
if (startsWithClashingQuote) str += "\`";
84+
str += content.slice(startOffset, content.length - endOffset);
85+
if (endsWithClashingQuote) str += "\`";
86+
str += "`";
87+
return str;
88+
}
89+
6990
/**
7091
* Marshals an object into a string.
7192
* @param {object} data
7293
* @returns {string}
7394
*/
7495
private marshalObjectToString<T> (data: { [key: string]: T }): string {
75-
return JSON.stringify(data);
96+
let str = "{";
97+
const space = " ";
98+
const keys = Object.keys(data);
99+
keys.forEach((key, index) => {
100+
str += `"${key}":${space}`;
101+
const value = data[key];
102+
const isString = typeof this.marshal(value) === "string";
103+
const marshalled = this.marshalToString(value);
104+
str += isString ? this.quoteIfNecessary(<string>marshalled) : marshalled;
105+
if (index !== keys.length - 1) str += `,${space}`;
106+
});
107+
str += "}";
108+
return str;
109+
// return JSON.stringify(data);
76110
}
77111

78112
/**
@@ -531,6 +565,7 @@ export class Marshaller implements IMarshaller {
531565
let trimmed = primitive
532566
.replace(/([{,:}"\]])([ \t\r\n]*)/g, (_, p1) => `${p1}`)
533567
.replace(/([{,])(\w+)(:)/g, (_, p1, p2, p3) => `${p1}"${p2}"${p3}`)
568+
.replace(/`([^`]*)`/g, (_, p1) => `"${p1}"`)
534569
.trim();
535570
if (trimmed.endsWith(";")) trimmed = trimmed.slice(0, trimmed.length - 1);
536571
return this.marshalStringToObject(trimmed, ++attempt);

test/Marshaller.test.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,4 +129,61 @@ test(`'marshal()' string -> best guess. #1`, t => {
129129
const input = `"1"`;
130130

131131
t.deepEqual<Object|null|undefined>(marshaller.marshal(input), expected);
132+
});
133+
134+
test(`'marshal()' object -> string. #1`, t => {
135+
const expected = '{"a": 2}';
136+
const input = {
137+
a: 2
138+
};
139+
140+
t.deepEqual<Object|null|undefined>(marshaller.marshal(input, expected), expected);
141+
});
142+
143+
test(`'marshal()' object -> string. #2`, t => {
144+
const expected = '{"a": 2, "b": () => {}}';
145+
const input = {
146+
a: 2,
147+
b: () => {}
148+
};
149+
150+
t.deepEqual<Object|null|undefined>(marshaller.marshal(input, expected), expected);
151+
});
152+
153+
test(`'marshal()' object -> string. #3`, t => {
154+
const expected = '{"c": {"d": `hello sir!`}}';
155+
const input = {
156+
c: {
157+
d: "hello sir!"
158+
}
159+
};
160+
161+
t.deepEqual<Object|null|undefined>(marshaller.marshal(input, expected), expected);
162+
});
163+
164+
test(`'marshal()' object -> string. #4`, t => {
165+
const expected = '{"foo": false, "type": {"expression": `hello`}}';
166+
const foo = false;
167+
const exp = "hello";
168+
const input = {
169+
foo,
170+
type: {
171+
expression: exp
172+
}
173+
};
174+
175+
t.deepEqual<Object|null|undefined>(marshaller.marshal(input, expected), expected);
176+
});
177+
178+
test(`'marshal()' string -> object. #1`, t => {
179+
const expected = {
180+
foo: false,
181+
type: {
182+
expression: "hello"
183+
}
184+
};
185+
186+
const input = '{"foo": false, "type": {"expression": `hello`}}';
187+
188+
t.deepEqual<Object|null|undefined>(marshaller.marshal(input, expected), expected);
132189
});

0 commit comments

Comments
 (0)