/// <summary> /// Matches a JSON quoted string without setting an error if match fails. /// </summary> /// <param name="this">This <see cref="VirtualStringMatcher"/>.</param> /// <param name="content">Extracted content.</param> /// <param name="allowNull">True to allow 'null'.</param> /// <returns><c>true</c> when matched, <c>false</c> otherwise.</returns> public static bool TryMatchJSONQuotedString(this VirtualStringMatcher @this, out string content, bool allowNull = false) { content = null; if (@this.IsEnd) { return(false); } long i = @this.StartIndex; if (@this.Text[i++] != '"') { return(allowNull && @this.TryMatchText("null")); } long len = @this.Length - 1; StringBuilder b = null; while (len >= 0) { if (len == 0) { return(false); } char c = @this.Text[i++]; --len; if (c == '"') { break; } if (c == '\\') { if (len == 0) { return(false); } if (b == null) { b = new StringBuilder(@this.Text.GetText(@this.StartIndex + 1, (int)(i - @this.StartIndex - 2))); } switch ((c = @this.Text[i++])) { case 'r': c = '\r'; break; case 'n': c = '\n'; break; case 'b': c = '\b'; break; case 't': c = '\t'; break; case 'f': c = '\f'; break; case 'u': { if (--len == 0) { return(false); } int cN = ReadHexDigit(@this.Text[i++]); if (cN < 0 || cN > 15) { return(false); } int val = cN << 12; if (--len == 0) { return(false); } cN = ReadHexDigit(@this.Text[i++]); if (cN < 0 || cN > 15) { return(false); } val |= cN << 8; if (--len == 0) { return(false); } cN = ReadHexDigit(@this.Text[i++]); if (cN < 0 || cN > 15) { return(false); } val |= cN << 4; if (--len == 0) { return(false); } cN = ReadHexDigit(@this.Text[i++]); if (cN < 0 || cN > 15) { return(false); } val |= cN; c = (char)val; break; } } } if (b != null) { b.Append(c); } } long lenS = i - @this.StartIndex; if (b != null) { content = b.ToString(); } else { content = @this.Text.GetText(@this.StartIndex + 1, (int)lenS - 2); } return(@this.UncheckedMove(lenS)); }