Skip to content

Commit

Permalink
Fixed json body parsing. #357
Browse files Browse the repository at this point in the history
  • Loading branch information
distantcam committed Oct 14, 2014
1 parent 9001ce2 commit fae91a4
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 75 deletions.
132 changes: 62 additions & 70 deletions src/ServiceInsight.Desktop/CodeParser/JsonParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,43 +2,64 @@
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Windows.Documents;
using System.Windows.Media;
using Newtonsoft.Json;

public class JsonParser : BaseParser
{
protected static char[] JsonSymbol = { ':', '[', ']', ',', '{', '}' };
protected static char[] JsonQuotes = { '"' };

protected bool IsInsideBlock;

protected override List<CodeLexem> Parse(SourcePart text)
{
var list = new List<CodeLexem>();

while (text.Length > 0)
{
var length = text.Length;
TryExtract(ref text, ByteOrderMark);

TryExtract(ref text, ByteOrderMark);
TryExtract(list, ref text, "[", LexemType.Symbol);
TryExtract(list, ref text, "{", LexemType.Symbol);
var s = text.Substring(0, text.Length);

if (TryExtract(list, ref text, "\"", LexemType.Quotes))
using (var sreader = new StringReader(s))
using (var reader = new JsonTextReader(sreader))
{
reader.DateParseHandling = DateParseHandling.None;

while (reader.Read())
{
ParseJsonPropertyName(list, ref text); // Extract Name
TryExtract(list, ref text, "\"", LexemType.Quotes);
TryExtract(list, ref text, ":", LexemType.Symbol);
TrySpace(list, ref text);
TryExtractValue(list, ref text); // Extract Value
switch (reader.TokenType)
{
case JsonToken.StartArray: Extract(list, ref text, "[", LexemType.Symbol); break;
case JsonToken.EndArray: Extract(list, ref text, "]", LexemType.Symbol); break;

case JsonToken.StartObject: Extract(list, ref text, "{", LexemType.Symbol); break;
case JsonToken.EndObject: Extract(list, ref text, "}", LexemType.Symbol); break;

case JsonToken.PropertyName: Extract(list, ref text, reader.Value.ToString(), LexemType.Property); break;

case JsonToken.Raw:
case JsonToken.Integer:
case JsonToken.Float:
case JsonToken.String:
case JsonToken.Boolean:
case JsonToken.Date:
case JsonToken.Bytes:
Extract(list, ref text, reader.Value.ToString(), LexemType.Value);
break;

case JsonToken.Null:
Extract(list, ref text, "null", LexemType.Value);
break;

case JsonToken.Comment: Extract(list, ref text, reader.Value.ToString(), LexemType.Comment); break;

default: list.Add(new CodeLexem(LexemType.Error, reader.TokenType.ToString())); break;
}
}
}

ParseSymbol(list, ref text); // Parse extras
TrySpace(list, ref text);
TryExtract(list, ref text, "\r\n", LexemType.LineBreak);
TryExtract(list, ref text, "\n", LexemType.LineBreak);
TryExtract(list, ref text, "}", LexemType.Symbol);
TryExtract(list, ref text, "]", LexemType.Symbol);
while (text.Length > 0)
{
var length = text.Length;

ExtractUnparsedItems(list, ref text);

if (length == text.Length)
break;
Expand All @@ -47,60 +68,27 @@ protected override List<CodeLexem> Parse(SourcePart text)
return list;
}

void TryExtractValue(List<CodeLexem> res, ref SourcePart text)
void Extract(List<CodeLexem> res, ref SourcePart text, string lex, LexemType type)
{
if (text[0] == '{')
{
res.Add(new CodeLexem(LexemType.Symbol, CutString(ref text, 1)));
}
else if (text[0] == '[')
while (!TryExtract(res, ref text, lex, type))
{
res.Add(new CodeLexem(LexemType.Symbol, CutString(ref text, 1)));
}
else if (text[0] == '"')
{
res.Add(new CodeLexem(LexemType.Quotes, CutString(ref text, 1)));
var end = text.IndexOf('"');
res.Add(new CodeLexem(LexemType.Value, CutString(ref text, end)));
res.Add(new CodeLexem(LexemType.Quotes, CutString(ref text, 1)));
}
else
{
var endOfValueQuote = text.IndexOfAny(new[] { '\"' });
if (endOfValueQuote > 0)
{
res.Add(new CodeLexem(LexemType.Value, CutString(ref text, endOfValueQuote)));
res.Add(new CodeLexem(LexemType.Quotes, CutString(ref text, 1)));
}
else
{
var endOfValueOrCollection = text.IndexOfAny(new[] { ',', '}' });
if (endOfValueOrCollection > 0)
{
res.Add(new CodeLexem(LexemType.Value, CutString(ref text, endOfValueOrCollection)));
res.Add(new CodeLexem(LexemType.Symbol, CutString(ref text, 1)));
}
}
}
}
var length = text.Length;

void ParseSymbol(ICollection<CodeLexem> res, ref SourcePart text)
{
var index = text.IndexOfAny(JsonSymbol);
if (index != 0)
return;
ExtractUnparsedItems(res, ref text);

res.Add(new CodeLexem(LexemType.Symbol, text.Substring(0, 1)));
text = text.Substring(1);
if (length == text.Length)
break;
}
}

void ParseJsonPropertyName(ICollection<CodeLexem> res, ref SourcePart text)
void ExtractUnparsedItems(List<CodeLexem> res, ref SourcePart text)
{
var index = text.IndexOf("\":");
if (index <= 0)
return;

res.Add(new CodeLexem(LexemType.Property, CutString(ref text, index)));
TrySpace(res, ref text);
TryExtract(res, ref text, "\r\n", LexemType.LineBreak);
TryExtract(res, ref text, "\n", LexemType.LineBreak);
TryExtract(res, ref text, "\"", LexemType.Quotes);
TryExtract(res, ref text, ",", LexemType.Symbol);
TryExtract(res, ref text, ":", LexemType.Symbol);
}

public override Inline ToInline(CodeLexem codeLexem, Brush brush)
Expand All @@ -112,15 +100,19 @@ public override Inline ToInline(CodeLexem codeLexem, Brush brush)
case LexemType.Symbol:
case LexemType.Object:
return CreateRun(codeLexem.Text, Colors.LightGray);

case LexemType.Property:
return CreateRun(codeLexem.Text, Colors.Blue);

case LexemType.Value:
case LexemType.Space:
case LexemType.PlainText:
case LexemType.String:
return CreateRun(codeLexem.Text, Colors.Black);

case LexemType.LineBreak:
return new LineBreak();

case LexemType.Complex:
case LexemType.Quotes:
return CreateRun(codeLexem.Text, Colors.Brown);
Expand Down
29 changes: 25 additions & 4 deletions src/ServiceInsight.Tests/JsonParserTests.cs
Original file line number Diff line number Diff line change
@@ -1,34 +1,54 @@
namespace Particular.ServiceInsight.Tests
{
using System.Linq;
using Desktop.CodeParser;
using NUnit.Framework;
using System.Linq;
using Shouldly;

[TestFixture]
public class JsonParserTests
{
[Test]
public void should_handle_null_value_without_quotes()
{
const string TestMessage = "{\"Id\":\"outer\",\"SubMessages\":[{\"Id\":\"inner1\",\"SubMessages\":null},{\"Id\":\"inner2\",\"SubMessages\":null}]}";

var lexemes = new CodeLexem(TestMessage).Parse(CodeLanguage.Json);
var values = lexemes.Where(lx => lx.Type == LexemType.Value).ToList();

lexemes.Count.ShouldBe(48);
values.Count.ShouldBe(5);
values[0].Text.ShouldBe("outer");
values[1].Text.ShouldBe("inner1");
values[2].Text.ShouldBe("null");
values[3].Text.ShouldBe("inner2");
values[4].Text.ShouldBe("null");
}

[Test]
public void should_parse_values_from_json()
{
const string TestMessage = "[\n \"shiny\",\n \"day\",\n \"need\"\n]";
const string TestMessage = "[\n \"shiny\",\n \"day\",\n \"need\",\n null]";

var lexemes = new CodeLexem(TestMessage).Parse(CodeLanguage.Json);
var values = lexemes.Where(lx => lx.Type == LexemType.Value).ToList();

values.Count.ShouldBe(3);
lexemes.Count.ShouldBe(24);
values.Count.ShouldBe(4);
values[0].Text.ShouldBe("shiny");
values[1].Text.ShouldBe("day");
values[2].Text.ShouldBe("need");
values[3].Text.ShouldBe("null");
}

[Test]
public void should_parse_properties_objects_and_symbols()
{
const string TestMessage = "[{\"$type\":\"NSB.Messages.CRM.RegisterCustomer, NSB.Messages\",\"Name\":\"Hadi\",\"Password\":\"123456\",\"EmailAddress\":\"h.eskandari@gmail.com\",\"RegistrationDate\":\"2013-01-28T03:24:05.0546437Z\"}]";
const string TestMessage = "[{\"$type\":\"NSB.Messages.CRM.RegisterCustomer, NSB.Messages\",\"Name\":\"Hadi\",\"Password\":\"123456\",\"EmailAddress\":\"h.eskandari@gmail.com\",\"RegistrationDate\":\"2013-01-28T03:24:05.0546437Z\"}]";

var lexemes = new CodeLexem(TestMessage).Parse(CodeLanguage.Json);

lexemes.Count().ShouldBe(44);
lexemes.Count(lx => lx.Type == LexemType.Property).ShouldBe(5);
lexemes.Count(lx => lx.Type == LexemType.Value).ShouldBe(5);
lexemes.Count(lx => lx.Type == LexemType.Quotes).ShouldBe(20);
Expand All @@ -52,6 +72,7 @@ public void should_parse_complex_json_graphs()

var lexemes = new CodeLexem(JsonGraph).Parse(CodeLanguage.Json);

lexemes.Count().ShouldBe(123);
lexemes.Count(lx => lx.Type == LexemType.Property).ShouldBe(11);
lexemes.Count(lx => lx.Type == LexemType.Value).ShouldBe(8);
lexemes.Count(lx => lx.Type == LexemType.LineBreak).ShouldBe(11);
Expand Down
1 change: 0 additions & 1 deletion src/ServiceInsight.sln
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ Global
{D55B9DD2-EA36-4A3B-B6C6-61933AD66933}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D55B9DD2-EA36-4A3B-B6C6-61933AD66933}.Release|Any CPU.Build.0 = Release|Any CPU
{63786CE5-D899-43DF-AD4E-C2CA3BAC3FC2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{63786CE5-D899-43DF-AD4E-C2CA3BAC3FC2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{63786CE5-D899-43DF-AD4E-C2CA3BAC3FC2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{63786CE5-D899-43DF-AD4E-C2CA3BAC3FC2}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
Expand Down

0 comments on commit fae91a4

Please sign in to comment.