private function normalize($jsonString)
    {
        if (!is_string($jsonString)) {
            throw new \InvalidArgumentException(sprintf(
                'Expected a string, got "%s"',
                gettype($jsonString)
            ));
        }
        $chars = str_split($jsonString);
        $inRight = $inQuote = $inFakeQuote = false;
        $fakeQuoteStart = null;

        if (empty($chars)) {
            return;
        }

        if ($chars[0] !== '{') {
            array_unshift($chars, '{');
            $chars[] = '}';
        }

        for ($index = 0; $index < count($chars); $index++) {
            $char = $chars[$index];
            $prevChar = isset($chars[$index - 1]) ? $chars[$index - 1] : null;

            if (!$inQuote && $prevChar == ':') {
                $inRight = true;
            }

            if (!$inQuote && preg_match('{\{,}', $char)) {
                $inRight = false;
            }

            // detect start of unquoted string
            if (!$inQuote && preg_match('{[\$a-zA-Z0-9]}', $char)) {
                array_splice($chars, $index, 0, '"');
                $fakeQuoteStart = $index;
                $index++;
                $inQuote = $inFakeQuote = true;

                continue;
            }

            // if we added a "fake" quote, look for the end of the unquoted string
            if ($inFakeQuote && preg_match('{[\s:\}\],]}', $char)) {

                // if we are on the left side, then "]" is OK.
                if (!$inRight && $char === ']') {
                    continue;
                }

                if ($inRight) {

                    // extract the right hand value
                    $string = implode('', array_slice($chars, $fakeQuoteStart + 1, $index - 1 - $fakeQuoteStart));

                    // if it is a number, then we don't quote it
                    if (is_numeric($string)) {
                        unset($chars[$fakeQuoteStart]);
                        $chars = array_values($chars);
                        $inQuote = $inFakeQuote = false;
                        $index--;

                        continue;
                    }

                    // if it is a boolean, then we don't quote it
                    if (in_array($string, ['true', 'false'])) {
                        unset($chars[$fakeQuoteStart]);
                        $chars = array_values($chars);
                        $inQuote = $inFakeQuote = false;
                        $index--;

                        continue;
                    }
                }

                // add the ending quote
                array_splice($chars, $index, 0, '"');
                $index++;
                $inQuote = $inFakeQuote = false;

                continue;
            }

            // enter standard quote mode
            if (!$inQuote && $char === '"') {
                $inQuote = true;

                continue;
            }

            // if we are in quote mode and encounter a closing quote, and the last character
            // was not the escape character
            if ($inQuote && $char === '"' && $prevChar !== '\\') {
                $inQuote = $inFakeQuote = false;

                continue;
            }
        }

        // if we were in a fake quote and hit the end, then add the closing quote
        if ($inFakeQuote) {
            $chars[] = '"';
        }

        $normalized = implode('', $chars);

        return $normalized;
    }