Search code examples
javascriptjsonparsingdata-extraction

Replace single quotes, but not if within double quotes to use JSON.parse()


I need a solution to only exchange pairs of single quotes, which are NOT within a pair of double quotes.

A simplified example (original is > 100 KB):

['Element1',"Element's2",{"Element3":"E3"},'Element4']

In this example Element's2 should not become Element"s2, but 'Element1' should become "Element1".

Background: I need to extract some website data contained in big Javascript blocks. To get it i need JOSN.parse() which does not accept single quotes (') as quotes.

but i can not simply use replace(/'/g, '"'), because the single quote is also used as apostrophe in some texts like: "that's it".

BTW: As @hakre recommenced, a much more simple solution for my case was to use eval() instead of JOSN.parse(). With eval() there was no need to exchange the single quotes.


Solution

  • I made a second variant, which also cares about escaped quotes like:[\'Element1\',"Element's2",{"Elements3":"E3"},'Element\'s4']

    but it is a bit slower and the code looks a bit more complicated (so i use the other variant):

    function convert(input) {
    
        var openQuote 
        var close = {symbol:null,level:0,firstPos:null}
        openQuote = close;
        const singleQuote = "'"
        const doubleQuote = '"'
        const replaceBy = '"'
        var pairCount = 0
        var data = Array.from(input)
    
        function replacePair(data, newChar, firstPos, secondPos) {
            data[firstPos] =  newChar;
            data[secondPos] =  newChar;
            pairCount++
        }
    
        function checkEscaped (data, pos, level = 0) {
            if (pos <= 0) return level
            if (data[pos-1] ==  "\\")  { return checkEscaped(data,pos-1,level+1) }
            return level
        }
    
        function checkQuotes(data,pos){
    
            if (data[pos] == singleQuote) {
            let level = checkEscaped(data, pos)
    
              if (openQuote.symbol == null) {openQuote = {symbol: singleQuote, level: level, firstPos: pos}; return}
              if (openQuote.symbol == singleQuote && openQuote.level == level) {replacePair(data,replaceBy,openQuote.firstPos,pos); openQuote = close; return;}
              return
            }
    
            if (data[pos] == doubleQuote) {
            let level = checkEscaped(data, pos)
    
              if (openQuote.symbol == null) {openQuote = {symbol: doubleQuote, level: level, firstPos: pos}; return}
              if (openQuote.symbol == doubleQuote && openQuote.level == level) {openQuote = close; return;}
            }
        }
    
        for (let i=0; i < data.length; i++) {
          checkQuotes(data,i)
        }
    
        var result = data.join("");
        console.log(pairCount , "Pairs of Single Quotes exchanged")
        return result
    }
    
    document.getElementById('start').addEventListener('click', () => {
        var dataIn = document.getElementById('input').value;
        var output = document.getElementById('output').value = convert(dataIn)
    });
    <html><body>
    <button id="start">start</button><BR>
    
    Input:<br><textarea id="input" name="w3review" rows="2" cols="100" spellcheck="false">
    [\'Element1\',"Element's2",{"Elements3":"E3"},'Element\'s4']
    </textarea><BR>
    
    Output:<br><textarea id="output" name="w3review" rows="2" cols="100" spellcheck="false"></textarea>
     
    </body></html>