@clynn-fe/akfe-editor-jsonf
Version:
JSON methods support function parse and stringify
147 lines (140 loc) • 4.17 kB
text/typescript
import {
EInfinityString,
ETypeStrings,
LeFE_Prefix,
Segment,
UndefinedReplacer
} from './defines'
import {
isArray,
isDate,
isMap,
isObject,
isSet,
isString
} from './type_detectors'
const parseReplaceString = (str: string) => {
// 由于 dataString 里可能还有 Segment,这里的意思是只取前两个,剩下的,还给拼起来,留着下面用
const [, type, ...dataStringArr] = str.split(Segment)
const dataString = dataStringArr.join(Segment)
return {
type,
dataString
}
}
const stringParse = (str: string, parse: (json: string) => any) => {
if (str.startsWith(LeFE_Prefix)) {
const { type, dataString } = parseReplaceString(str)
switch (type) {
// undefined 比较坑,这里不处理,最后遍历统一处理
// case ETypeStrings.undefined:
// return undefined
case ETypeStrings.Infinity:
return dataString === EInfinityString.positive ? Infinity : -Infinity
case ETypeStrings.NaN:
return NaN
case ETypeStrings.BigInt:
try {
return BigInt(dataString)
} catch (error) {
console.error(error)
return BigInt(0)
}
case ETypeStrings.Symbol:
return Symbol(dataString)
case ETypeStrings.Function:
try {
return Function(`return ${dataString}`)()
} catch (error) {
console.error(error)
return function placeholder() {
console.warn('注意!该函数在 JSON 解析时出现错误。')
}
}
case ETypeStrings.Date:
return new Date(Number(dataString))
case ETypeStrings.Set:
try {
return new Set(parse(dataString))
} catch (error) {
console.error(error)
return new Set()
}
case ETypeStrings.Map:
try {
return new Map(parse(dataString))
} catch (error) {
console.error(error)
return new Map()
}
default:
return str
}
} else {
/* Start: 老 Function 逻辑兼容 */
if (str.startsWith('function')) {
try {
return Function(`return ${str}`)()
} catch (error) {
console.error(error)
return function placeholder() {
console.warn('注意!该函数在 JSON 解析时出现错误。')
}
}
}
/* End: 老 Function 逻辑兼容 */
return str
}
}
const isTypesNeededTraverse = (
val: unknown
): val is unknown[] | Record<string, unknown> =>
(isObject(val) && !isDate(val) && !isSet(val) && !isMap(val)) || isArray(val)
const needNotTraverseHandler = (val: unknown) =>
isString(val)
? stringParse(val, parse)
: // 在无需遍历的类型中,除了 string 就是 boolean, number, null, 直接返回
val
const undefinedHandler = (
val: unknown[] | Record<string, unknown>
): unknown[] | Record<string, unknown> =>
isObject(val)
? Object.entries(val).reduce((prev, [k, v]) => {
if (isTypesNeededTraverse(v)) {
return Object.assign(prev, {
[k]: undefinedHandler(v)
})
} else {
const { type } =
isString(v) && v.startsWith(LeFE_Prefix)
? parseReplaceString(v)
: {
type: ''
}
return Object.assign(prev, {
[k]:
type === ETypeStrings.undefined || v === UndefinedReplacer
? undefined
: v
})
}
}, {})
: val.map(item => {
if (isTypesNeededTraverse(item)) {
return undefinedHandler(item)
} else {
const { type } =
isString(item) && item.startsWith(LeFE_Prefix)
? parseReplaceString(item)
: {
type: ''
}
return type === ETypeStrings.undefined ? undefined : item
}
})
export const parse = (json: string): any => {
const result = JSON.parse(json, (key: string, val: string) => {
return !isTypesNeededTraverse(val) ? needNotTraverseHandler(val) : val
})
return undefinedHandler(result)
}