使用clarinet(browser&node.js)解析重複key值的json字串

643104191發表於2022-02-22

簡單粗暴,直接上程式碼,
會把{"a":1,"a":2}解析成{"a":[1,2]}
附上npm連結:clarinet
原文連結segmentfault

var clarinet = require("clarinet");

class InvalidJSON {
    result = {}
    /**
     * @description 進入的括號層數
     */
    bracketCount = 0
    /**
     * @type { Array<{ type: 'array'; value: any[] } | { type: 'object'; arrayKeys: string[]; arrayKeysMap: { [key: string]: boolean; }; keys: string[]; value: { [key: string]: any } }> }
     */
    queue = []
    
    get lastQueue() {
        const queue = this.queue
        return queue[queue.length - 1]
    }
    
    queuePop() {
        this.queue.pop()
        this.bracketCount--
    }
    
    queuePush(item) {
        this.queue.push(item)
        this.bracketCount++
    }
    
    onopenobject(key) {
        // console.log('onopenobject', key)
        const value = {}
        this.bracketCount === 0 && (this.result = value)
        this.lastQueue && this.onvalue(value)
        this.queuePush({ keys: [key], arrayKeys: [], arrayKeysMap: {}, type: 'object', value })
    }
    
    onkey(key) {
        // console.log('onkey', key)
        const { keys, type, arrayKeys } = this.lastQueue
        if (type !== 'object') { return }
        keys.includes(key) && arrayKeys.push(key)
        keys.push(key)
    }
    
    onvalue(v) {
        // console.log('onvalue', v)
        const { type, value, keys, arrayKeysMap, arrayKeys } = this.lastQueue
        if (type === 'array') {
            value.push(v)
        } else if (type === 'object') {
            const lastKeys = keys[keys.length - 1]
            if (arrayKeys.includes(lastKeys)) {
                if (!arrayKeysMap[lastKeys]) {
                    arrayKeysMap[lastKeys] = true
                    value[lastKeys] = [value[lastKeys]]
                }
                value[lastKeys].push(v)
            } else {
                value[lastKeys] = v
            }
        }
    }
    
    oncloseobject() {
        // console.log('onclosearray')
        this.queuePop()
    }
    
    onopenarray() {
        // console.log('onopenarray')
        const value = []
        this.bracketCount === 0 && (this.result = value)
        this.lastQueue && this.onvalue(value)
        this.queuePush({ type: 'array', value })
    }
    
    onclosearray() {
        // console.log('onclosearray')
        this.queuePop()
    }

    /**
     * @param { string } json 不合法的json
     */
    parse(json) {
        this.bracketCount = 0
        this.result = {}
        this.queue = []
        this.parser.write(json).close()
        return this.result
    }
    
    parser

    constructor() {
        const parser = clarinet.parser()
        const parserEvents = ['value', 'openobject', 'key', 'closeobject', 'openarray', 'closearray']
        parserEvents.forEach(event => {
            const eventName = `on${event}`
            parser[eventName] = (...args) => this[eventName](...args)
        })
        this.parser = parser
    }
}

const invalidJSON = new InvalidJSON()

console.log(JSON.stringify(invalidJSON.parse('{"p":7,"p": 9,"w":3,"q":[0,8],"g":77,"g":{"a":100,"d":{"z":2},"d":{"z":3}}}')))

原理:clarinet會解析json字串,在每個json字串關鍵點觸發回撥,
如開始一個object,進入一個key,進入一個value,開始一個array,
利用這些回撥,拼裝出期望的格式

相關文章