I have a method that takes a JSON string as input and "fixes" it. By that, I mean, that the JSON string comes in with all values quoted. My method un-quotes the values that shouldn't be quoted (i.e. true, false, numbers, {}, etc). The method was originally doing it by doing 4 string replaces (for the true / false, etc) and regex replace for the numbers. Kind of slow. So I re-wrote it with StringBuilder
:
protected static string FixupJson(string json)
{
if (json == null)
throw new ArgumentNullException(nameof(json));
int nLen = json.Length;
StringBuilder sb = new StringBuilder(nLen);
for (int i = 0; i < nLen; i++)
{
char ch = json[i];
if (ch == ':' && json[i+1] == ' ' && json[i + 2] == '\"')
{
int k = json.IndexOf('\"', i + 3);
sb.Append(": ");
string g = json.Substring(i + 3, k - i - 3);
bool bQuote = true;
if (g == "true" || g == "false" || g == "null" || g == "{}")
bQuote = false;
else if (g == "NaN")
bQuote = true;
else if (Double.TryParse(g, out double d))
bQuote = false;
if (bQuote)
sb.Append('\"');
sb.Append(g);
if (bQuote)
sb.Append('\"');
i = k;
}
else
{
sb.Append(ch);
}
}
return sb.ToString();
}
This got me a 35% improvement, but I want to see if there is a better approach or if I can get this faster. My first StringBuilder
approach, instead of appending char by char would append chunks pulled out with SubString()
. That was actually a lot slower then this version.
Yes, I understand the right way to do it would be to fix it at the source :), which I do control, but the way the json gets serialized out at the source, I don't know if the value needs to be quoted until the json is fully spit out (or at least until the value is fully spit out) as a single value can be a formatted string from multiple sources (i.e. "myVal": "(this part from one place)|(this part from another place))". So "fixing it there" would mean going from a single String.Format()
for the entire json to a bunch of them for each value. Right now the JSON is "pre-rendered" (once) and passed into a String.Format()
to fill in the values, so by fixing it there I'd have to go to a bunch of little String.Format()
's to format each value then pass it in to the big String.Format()
… so I figure this is a decent compromise.
EDIT: Should handle any old json, but this was the one I was using in my test app in C# string format ready to paste :).
"{\r\n \"xxx\": \"TRACE|1\",\r\n \"Timestamp\": \"2019-01-01 12:44:36.529\",\r\n \"Message\": {\r\n \"a\": \"bbb\",\r\n \"b1\": {\r\n \"b2\": \"test2\",\r\n \"b3\": \"Test1\",\r\n \"b4\": \"true\",\r\n \"b5\": \"178\"\r\n }\r\n },\r\n \"ThreadId\": \"1\",\r\n \"Exception\": \"{}\"\r\n}"