JSON formatter in C#? - c#

Looking for a function that will take a string of Json as input and format it with line breaks and indentations. Validation would be a bonus, but isn't necessary, and I don't need to parse it into an object or anything.
Anyone know of such a library?
Sample input:
{"status":"OK", "results":[ {"types":[ "locality", "political"], "formatted_address":"New York, NY, USA", "address_components":[ {"long_name":"New York", "short_name":"New York", "types":[ "locality", "political"]}, {"long_name":"New York", "short_name":"New York", "types":[ "administrative_area_level_2", "political"]}, {"long_name":"New York", "short_name":"NY", "types":[ "administrative_area_level_1", "political"]}, {"long_name":"United States", "short_name":"US", "types":[ "country", "political"]}], "geometry":{"location":{"lat":40.7143528, "lng":-74.0059731}, "location_type":"APPROXIMATE", "viewport":{"southwest":{"lat":40.5788964, "lng":-74.2620919}, "northeast":{"lat":40.8495342, "lng":-73.7498543}}, "bounds":{"southwest":{"lat":40.4773990, "lng":-74.2590900}, "northeast":{"lat":40.9175770, "lng":-73.7002720}}}}]}

You could also use the Newtonsoft.Json library for this and call SerializeObject with the Formatting.Indented enum -
var x = JsonConvert.SerializeObject(jsonString, Formatting.Indented);
Documentation: Serialize an Object
Update -
Just tried it again. Pretty sure this used to work - perhaps it changed in a subsequent version or perhaps i'm just imagining things. Anyway, as per the comments below, it doesn't quite work as expected. These do, however (just tested in linqpad). The first one is from the comments, the 2nd one is an example i found elsewhere in SO -
void Main()
{
//Example 1
var t = "{\"x\":57,\"y\":57.0,\"z\":\"Yes\"}";
var obj = Newtonsoft.Json.JsonConvert.DeserializeObject(t);
var f = Newtonsoft.Json.JsonConvert.SerializeObject(obj, Newtonsoft.Json.Formatting.Indented);
Console.WriteLine(f);
//Example 2
JToken jt = JToken.Parse(t);
string formatted = jt.ToString(Newtonsoft.Json.Formatting.Indented);
Console.WriteLine(formatted);
//Example 2 in one line -
Console.WriteLine(JToken.Parse(t).ToString(Newtonsoft.Json.Formatting.Indented));
}

I updated the old version, now it should support unquoted values such as integers and booleans.
I refactored the previous version and got the final version:
The code is shorter and cleaner. Only require one extension method. The most important: fixed some bugs.
class JsonHelper
{
private const string INDENT_STRING = " ";
public static string FormatJson(string str)
{
var indent = 0;
var quoted = false;
var sb = new StringBuilder();
for (var i = 0; i < str.Length; i++)
{
var ch = str[i];
switch (ch)
{
case '{':
case '[':
sb.Append(ch);
if (!quoted)
{
sb.AppendLine();
Enumerable.Range(0, ++indent).ForEach(item => sb.Append(INDENT_STRING));
}
break;
case '}':
case ']':
if (!quoted)
{
sb.AppendLine();
Enumerable.Range(0, --indent).ForEach(item => sb.Append(INDENT_STRING));
}
sb.Append(ch);
break;
case '"':
sb.Append(ch);
bool escaped = false;
var index = i;
while (index > 0 && str[--index] == '\\')
escaped = !escaped;
if (!escaped)
quoted = !quoted;
break;
case ',':
sb.Append(ch);
if (!quoted)
{
sb.AppendLine();
Enumerable.Range(0, indent).ForEach(item => sb.Append(INDENT_STRING));
}
break;
case ':':
sb.Append(ch);
if (!quoted)
sb.Append(" ");
break;
default:
sb.Append(ch);
break;
}
}
return sb.ToString();
}
}
static class Extensions
{
public static void ForEach<T>(this IEnumerable<T> ie, Action<T> action)
{
foreach (var i in ie)
{
action(i);
}
}
}

Shorter sample for json.net library.
using Newtonsoft.Json;
private static string format_json(string json)
{
dynamic parsedJson = JsonConvert.DeserializeObject(json);
return JsonConvert.SerializeObject(parsedJson, Formatting.Indented);
}
PS: You can wrap the formatted json text with tag to print as it is on the html page.

This worked for me using System.Text.Json in .Net Core 3.1
public string PrettyJson(string unPrettyJson)
{
var options = new JsonSerializerOptions(){
WriteIndented = true
};
var jsonElement = JsonSerializer.Deserialize<JsonElement>(unPrettyJson);
return JsonSerializer.Serialize(jsonElement, options);
}

Here's a compact version of a JSON beautifier.
private const string INDENT_STRING = " ";
static string FormatJson(string json) {
int indentation = 0;
int quoteCount = 0;
var result =
from ch in json
let quotes = ch == '"' ? quoteCount++ : quoteCount
let lineBreak = ch == ',' && quotes % 2 == 0 ? ch + Environment.NewLine + String.Concat(Enumerable.Repeat(INDENT_STRING, indentation)) : null
let openChar = ch == '{' || ch == '[' ? ch + Environment.NewLine + String.Concat(Enumerable.Repeat(INDENT_STRING, ++indentation)) : ch.ToString()
let closeChar = ch == '}' || ch == ']' ? Environment.NewLine + String.Concat(Enumerable.Repeat(INDENT_STRING, --indentation)) + ch : ch.ToString()
select lineBreak == null
? openChar.Length > 1
? openChar
: closeChar
: lineBreak;
return String.Concat(result);
}
Outputs:
{
"status":"OK",
"results":[
{
"types":[
"locality",
"political"
],
"formatted_address":"New York, NY, USA",
"address_components":[
{
"long_name":"New York",
"short_name":"New York",
"types":[
"locality",
"political"
]
},
{
"long_name":"New York",
"short_name":"New York",
"types":[
"administrative_area_level_2",
"political"
]
},
{
"long_name":"New York",
"short_name":"NY",
"types":[
"administrative_area_level_1",
"political"
]
},
{
"long_name":"United States",
"short_name":"US",
"types":[
"country",
"political"
]
}
],
"geometry":{
"location":{
"lat":40.7143528,
"lng":-74.0059731
},
"location_type":"APPROXIMATE",
"viewport":{
"southwest":{
"lat":40.5788964,
"lng":-74.2620919
},
"northeast":{
"lat":40.8495342,
"lng":-73.7498543
}
},
"bounds":{
"southwest":{
"lat":40.4773990,
"lng":-74.2590900
},
"northeast":{
"lat":40.9175770,
"lng":-73.7002720
}
}
}
}
]
}

I was very impressed by compact JSON formatter by Vince Panuccio.
Here is an improved version I now use:
public static string FormatJson(string json, string indent = " ")
{
var indentation = 0;
var quoteCount = 0;
var escapeCount = 0;
var result =
from ch in json ?? string.Empty
let escaped = (ch == '\\' ? escapeCount++ : escapeCount > 0 ? escapeCount-- : escapeCount) > 0
let quotes = ch == '"' && !escaped ? quoteCount++ : quoteCount
let unquoted = quotes % 2 == 0
let colon = ch == ':' && unquoted ? ": " : null
let nospace = char.IsWhiteSpace(ch) && unquoted ? string.Empty : null
let lineBreak = ch == ',' && unquoted ? ch + Environment.NewLine + string.Concat(Enumerable.Repeat(indent, indentation)) : null
let openChar = (ch == '{' || ch == '[') && unquoted ? ch + Environment.NewLine + string.Concat(Enumerable.Repeat(indent, ++indentation)) : ch.ToString()
let closeChar = (ch == '}' || ch == ']') && unquoted ? Environment.NewLine + string.Concat(Enumerable.Repeat(indent, --indentation)) + ch : ch.ToString()
select colon ?? nospace ?? lineBreak ?? (
openChar.Length > 1 ? openChar : closeChar
);
return string.Concat(result);
}
It fixes the following issues:
Escape sequences inside strings
Missing spaces after colon
Extra spaces after commas (or elsewhere)
Square and curly braces inside strings
Doesn't fail on null input
Outputs:
{
"status": "OK",
"results": [
{
"types": [
"locality",
"political"
],
"formatted_address": "New York, NY, USA",
"address_components": [
{
"long_name": "New York",
"short_name": "New York",
"types": [
"locality",
"political"
]
},
{
"long_name": "New York",
"short_name": "New York",
"types": [
"administrative_area_level_2",
"political"
]
},
{
"long_name": "New York",
"short_name": "NY",
"types": [
"administrative_area_level_1",
"political"
]
},
{
"long_name": "United States",
"short_name": "US",
"types": [
"country",
"political"
]
}
],
"geometry": {
"location": {
"lat": 40.7143528,
"lng": -74.0059731
},
"location_type": "APPROXIMATE",
"viewport": {
"southwest": {
"lat": 40.5788964,
"lng": -74.2620919
},
"northeast": {
"lat": 40.8495342,
"lng": -73.7498543
}
},
"bounds": {
"southwest": {
"lat": 40.4773990,
"lng": -74.2590900
},
"northeast": {
"lat": 40.9175770,
"lng": -73.7002720
}
}
}
}
]
}

There are already a bunch of great answers here that use Newtonsoft.JSON, but here's one more that uses JObject.Parse in combination with ToString(), since that hasn't been mentioned yet:
var jObj = Newtonsoft.Json.Linq.JObject.Parse(json);
var formatted = jObj.ToString(Newtonsoft.Json.Formatting.Indented);

All credits are to Frank Tzanabetis. However this is the shortest direct example, that also survives in case of empty string or broken original JSON string:
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
...
try
{
return JToken.Parse(jsonString).ToString(Formatting.Indented);
}
catch
{
return jsonString;

Even simpler one that I just wrote:
public class JsonFormatter
{
public static string Indent = " ";
public static string PrettyPrint(string input)
{
var output = new StringBuilder(input.Length * 2);
char? quote = null;
int depth = 0;
for(int i=0; i<input.Length; ++i)
{
char ch = input[i];
switch (ch)
{
case '{':
case '[':
output.Append(ch);
if (!quote.HasValue)
{
output.AppendLine();
output.Append(Indent.Repeat(++depth));
}
break;
case '}':
case ']':
if (quote.HasValue)
output.Append(ch);
else
{
output.AppendLine();
output.Append(Indent.Repeat(--depth));
output.Append(ch);
}
break;
case '"':
case '\'':
output.Append(ch);
if (quote.HasValue)
{
if (!output.IsEscaped(i))
quote = null;
}
else quote = ch;
break;
case ',':
output.Append(ch);
if (!quote.HasValue)
{
output.AppendLine();
output.Append(Indent.Repeat(depth));
}
break;
case ':':
if (quote.HasValue) output.Append(ch);
else output.Append(" : ");
break;
default:
if (quote.HasValue || !char.IsWhiteSpace(ch))
output.Append(ch);
break;
}
}
return output.ToString();
}
}
Necessary extensions:
public static string Repeat(this string str, int count)
{
return new StringBuilder().Insert(0, str, count).ToString();
}
public static bool IsEscaped(this string str, int index)
{
bool escaped = false;
while (index > 0 && str[--index] == '\\') escaped = !escaped;
return escaped;
}
public static bool IsEscaped(this StringBuilder str, int index)
{
return str.ToString().IsEscaped(index);
}
Sample output:
{
"status" : "OK",
"results" : [
{
"types" : [
"locality",
"political"
],
"formatted_address" : "New York, NY, USA",
"address_components" : [
{
"long_name" : "New York",
"short_name" : "New York",
"types" : [
"locality",
"political"
]
},
{
"long_name" : "New York",
"short_name" : "New York",
"types" : [
"administrative_area_level_2",
"political"
]
},
{
"long_name" : "New York",
"short_name" : "NY",
"types" : [
"administrative_area_level_1",
"political"
]
},
{
"long_name" : "United States",
"short_name" : "US",
"types" : [
"country",
"political"
]
}
],
"geometry" : {
"location" : {
"lat" : 40.7143528,
"lng" : -74.0059731
},
"location_type" : "APPROXIMATE",
"viewport" : {
"southwest" : {
"lat" : 40.5788964,
"lng" : -74.2620919
},
"northeast" : {
"lat" : 40.8495342,
"lng" : -73.7498543
}
},
"bounds" : {
"southwest" : {
"lat" : 40.4773990,
"lng" : -74.2590900
},
"northeast" : {
"lat" : 40.9175770,
"lng" : -73.7002720
}
}
}
}
]
}

Just use JsonDocument and Utf8JsonWriter. No third-party library required. No target object for deserialization for jsonString required.
using System.IO;
using System.Text;
using System.Text.Json;
// other code ...
public string Prettify(string jsonString)
{
using var stream = new MemoryStream();
var document = JsonDocument.Parse(jsonString);
var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Indented = true });
document.WriteTo(writer);
writer.Flush();
return Encoding.UTF8.GetString(stream.ToArray());
}

As benjymous pointed out, you can use Newtonsoft.Json with a temporary object and deserialize/serialize.
var obj = JsonConvert.DeserializeObject(jsonString);
var formatted = JsonConvert.SerializeObject(obj, Formatting.Indented);

The main reason of writing your own function is that JSON frameworks usually perform parsing of strings into .net types and converting them back to string, which may result in losing original strings. For example 0.0002 becomes 2E-4
I do not post my function (it's pretty same as other here) but here are the test cases
using System.IO;
using Newtonsoft.Json;
using NUnit.Framework;
namespace json_formatter.tests
{
[TestFixture]
internal class FormatterTests
{
[Test]
public void CompareWithNewtonsofJson()
{
string file = Path.Combine(TestContext.CurrentContext.TestDirectory, "json", "minified.txt");
string json = File.ReadAllText(file);
string newton = JsonPrettify(json);
// Double space are indent symbols which newtonsoft framework uses
string my = new Formatter(" ").Format(json);
Assert.AreEqual(newton, my);
}
[Test]
public void EmptyArrayMustNotBeFormatted()
{
var input = "{\"na{me\": []}";
var expected = "{\r\n\t\"na{me\": []\r\n}";
Assert.AreEqual(expected, new Formatter().Format(input));
}
[Test]
public void EmptyObjectMustNotBeFormatted()
{
var input = "{\"na{me\": {}}";
var expected = "{\r\n\t\"na{me\": {}\r\n}";
Assert.AreEqual(expected, new Formatter().Format(input));
}
[Test]
public void MustAddLinebreakAfterBraces()
{
var input = "{\"name\": \"value\"}";
var expected = "{\r\n\t\"name\": \"value\"\r\n}";
Assert.AreEqual(expected, new Formatter().Format(input));
}
[Test]
public void MustFormatNestedObject()
{
var input = "{\"na{me\":\"val}ue\", \"name1\": {\"name2\":\"value\"}}";
var expected = "{\r\n\t\"na{me\": \"val}ue\",\r\n\t\"name1\": {\r\n\t\t\"name2\": \"value\"\r\n\t}\r\n}";
Assert.AreEqual(expected, new Formatter().Format(input));
}
[Test]
public void MustHandleArray()
{
var input = "{\"name\": \"value\", \"name2\":[\"a\", \"b\", \"c\"]}";
var expected = "{\r\n\t\"name\": \"value\",\r\n\t\"name2\": [\r\n\t\t\"a\",\r\n\t\t\"b\",\r\n\t\t\"c\"\r\n\t]\r\n}";
Assert.AreEqual(expected, new Formatter().Format(input));
}
[Test]
public void MustHandleArrayOfObject()
{
var input = "{\"name\": \"value\", \"name2\":[{\"na{me\":\"val}ue\"}, {\"nam\\\"e2\":\"val\\\\\\\"ue\"}]}";
var expected =
"{\r\n\t\"name\": \"value\",\r\n\t\"name2\": [\r\n\t\t{\r\n\t\t\t\"na{me\": \"val}ue\"\r\n\t\t},\r\n\t\t{\r\n\t\t\t\"nam\\\"e2\": \"val\\\\\\\"ue\"\r\n\t\t}\r\n\t]\r\n}";
Assert.AreEqual(expected, new Formatter().Format(input));
}
[Test]
public void MustHandleEscapedString()
{
var input = "{\"na{me\":\"val}ue\", \"name1\": {\"nam\\\"e2\":\"val\\\\\\\"ue\"}}";
var expected = "{\r\n\t\"na{me\": \"val}ue\",\r\n\t\"name1\": {\r\n\t\t\"nam\\\"e2\": \"val\\\\\\\"ue\"\r\n\t}\r\n}";
Assert.AreEqual(expected, new Formatter().Format(input));
}
[Test]
public void MustIgnoreEscapedQuotesInsideString()
{
var input = "{\"na{me\\\"\": \"val}ue\"}";
var expected = "{\r\n\t\"na{me\\\"\": \"val}ue\"\r\n}";
Assert.AreEqual(expected, new Formatter().Format(input));
}
[TestCase(" ")]
[TestCase("\"")]
[TestCase("{")]
[TestCase("}")]
[TestCase("[")]
[TestCase("]")]
[TestCase(":")]
[TestCase(",")]
public void MustIgnoreSpecialSymbolsInsideString(string symbol)
{
string input = "{\"na" + symbol + "me\": \"val" + symbol + "ue\"}";
string expected = "{\r\n\t\"na" + symbol + "me\": \"val" + symbol + "ue\"\r\n}";
Assert.AreEqual(expected, new Formatter().Format(input));
}
[Test]
public void StringEndsWithEscapedBackslash()
{
var input = "{\"na{me\\\\\": \"val}ue\"}";
var expected = "{\r\n\t\"na{me\\\\\": \"val}ue\"\r\n}";
Assert.AreEqual(expected, new Formatter().Format(input));
}
private static string PrettifyUsingNewtosoft(string json)
{
using (var stringReader = new StringReader(json))
using (var stringWriter = new StringWriter())
{
var jsonReader = new JsonTextReader(stringReader);
var jsonWriter = new JsonTextWriter(stringWriter)
{
Formatting = Formatting.Indented
};
jsonWriter.WriteToken(jsonReader);
return stringWriter.ToString();
}
}
}
}

You need to skip \r and \n in PrettyPrint(). The output looks funny of there are some crlf's already present (or the source was already formatted).

Fixed it... somewhat.
public class JsonFormatter
{
#region class members
const string Space = " ";
const int DefaultIndent = 0;
const string Indent = Space + Space + Space + Space;
static readonly string NewLine = Environment.NewLine;
#endregion
private enum JsonContextType
{
Object, Array
}
static void BuildIndents(int indents, StringBuilder output)
{
indents += DefaultIndent;
for (; indents > 0; indents--)
output.Append(Indent);
}
bool inDoubleString = false;
bool inSingleString = false;
bool inVariableAssignment = false;
char prevChar = '\0';
Stack<JsonContextType> context = new Stack<JsonContextType>();
bool InString()
{
return inDoubleString || inSingleString;
}
public string PrettyPrint(string input)
{
var output = new StringBuilder(input.Length * 2);
char c;
for (int i = 0; i < input.Length; i++)
{
c = input[i];
switch (c)
{
case '{':
if (!InString())
{
if (inVariableAssignment || (context.Count > 0 && context.Peek() != JsonContextType.Array))
{
output.Append(NewLine);
BuildIndents(context.Count, output);
}
output.Append(c);
context.Push(JsonContextType.Object);
output.Append(NewLine);
BuildIndents(context.Count, output);
}
else
output.Append(c);
break;
case '}':
if (!InString())
{
output.Append(NewLine);
context.Pop();
BuildIndents(context.Count, output);
output.Append(c);
}
else
output.Append(c);
break;
case '[':
output.Append(c);
if (!InString())
context.Push(JsonContextType.Array);
break;
case ']':
if (!InString())
{
output.Append(c);
context.Pop();
}
else
output.Append(c);
break;
case '=':
output.Append(c);
break;
case ',':
output.Append(c);
if (!InString() && context.Peek() != JsonContextType.Array)
{
BuildIndents(context.Count, output);
output.Append(NewLine);
BuildIndents(context.Count, output);
inVariableAssignment = false;
}
break;
case '\'':
if (!inDoubleString && prevChar != '\\')
inSingleString = !inSingleString;
output.Append(c);
break;
case ':':
if (!InString())
{
inVariableAssignment = true;
output.Append(Space);
output.Append(c);
output.Append(Space);
}
else
output.Append(c);
break;
case '"':
if (!inSingleString && prevChar != '\\')
inDoubleString = !inDoubleString;
output.Append(c);
break;
case ' ':
if (InString())
output.Append(c);
break;
default:
output.Append(c);
break;
}
prevChar = c;
}
return output.ToString();
}
}
credit [dead link]

This will put each item on a new line
VB.NET
mytext = responseFromServer.Replace("{", vbNewLine + "{")
C#
mytext = responseFromServer.Replace("{", Environment.NewLine + "{");

This is a variant of the accepted answer that I like to use. The commented parts result in what I consider a more readable format (you would need to comment out the adjacent code to see the difference):
public class JsonHelper
{
private const int INDENT_SIZE = 4;
public static string FormatJson(string str)
{
str = (str ?? "").Replace("{}", #"\{\}").Replace("[]", #"\[\]");
var inserts = new List<int[]>();
bool quoted = false, escape = false;
int depth = 0/*-1*/;
for (int i = 0, N = str.Length; i < N; i++)
{
var chr = str[i];
if (!escape && !quoted)
switch (chr)
{
case '{':
case '[':
inserts.Add(new[] { i, +1, 0, INDENT_SIZE * ++depth });
//int n = (i == 0 || "{[,".Contains(str[i - 1])) ? 0 : -1;
//inserts.Add(new[] { i, n, INDENT_SIZE * ++depth * -n, INDENT_SIZE - 1 });
break;
case ',':
inserts.Add(new[] { i, +1, 0, INDENT_SIZE * depth });
//inserts.Add(new[] { i, -1, INDENT_SIZE * depth, INDENT_SIZE - 1 });
break;
case '}':
case ']':
inserts.Add(new[] { i, -1, INDENT_SIZE * --depth, 0 });
//inserts.Add(new[] { i, -1, INDENT_SIZE * depth--, 0 });
break;
case ':':
inserts.Add(new[] { i, 0, 1, 1 });
break;
}
quoted = (chr == '"') ? !quoted : quoted;
escape = (chr == '\\') ? !escape : false;
}
if (inserts.Count > 0)
{
var sb = new System.Text.StringBuilder(str.Length * 2);
int lastIndex = 0;
foreach (var insert in inserts)
{
int index = insert[0], before = insert[2], after = insert[3];
bool nlBefore = (insert[1] == -1), nlAfter = (insert[1] == +1);
sb.Append(str.Substring(lastIndex, index - lastIndex));
if (nlBefore) sb.AppendLine();
if (before > 0) sb.Append(new String(' ', before));
sb.Append(str[index]);
if (nlAfter) sb.AppendLine();
if (after > 0) sb.Append(new String(' ', after));
lastIndex = index + 1;
}
str = sb.ToString();
}
return str.Replace(#"\{\}", "{}").Replace(#"\[\]", "[]");
}
}

Example
public static string JsonFormatter(string json)
{
StringBuilder builder = new StringBuilder();
bool quotes = false;
bool ignore = false;
int offset = 0;
int position = 0;
if (string.IsNullOrEmpty(json))
{
return string.Empty;
}
json = json.Replace(Environment.NewLine, "").Replace("\t", "");
foreach (char character in json)
{
switch (character)
{
case '"':
if (!ignore)
{
quotes = !quotes;
}
break;
case '\'':
if (quotes)
{
ignore = !ignore;
}
break;
}
if (quotes)
{
builder.Append(character);
}
else
{
switch (character)
{
case '{':
case '[':
builder.Append(character);
builder.Append(Environment.NewLine);
builder.Append(new string(' ', ++offset * 4));
break;
case '}':
case ']':
builder.Append(Environment.NewLine);
builder.Append(new string(' ', --offset * 4));
builder.Append(character);
break;
case ',':
builder.Append(character);
builder.Append(Environment.NewLine);
builder.Append(new string(' ', offset * 4));
break;
case ':':
builder.Append(character);
builder.Append(' ');
break;
default:
if (character != ' ')
{
builder.Append(character);
}
break;
}
position++;
}
}
return builder.ToString().Trim();
}

This version produces JSON that is more compact and in my opinion more readable since you can see more at one time. It does this by formatting the deepest layer inline or like a compact array structure.
The code has no dependencies but is more complex.
{
"name":"Seller",
"schema":"dbo",
"CaptionFields":["Caption","Id"],
"fields":[
{"name":"Id","type":"Integer","length":"10","autoincrement":true,"nullable":false},
{"name":"FirstName","type":"Text","length":"50","autoincrement":false,"nullable":false},
{"name":"LastName","type":"Text","length":"50","autoincrement":false,"nullable":false},
{"name":"LotName","type":"Text","length":"50","autoincrement":false,"nullable":true},
{"name":"LotDetailsURL","type":"Text","length":"255","autoincrement":false,"nullable":true}
]
}
The code follows
private class IndentJsonInfo
{
public IndentJsonInfo(string prefix, char openingTag)
{
Prefix = prefix;
OpeningTag = openingTag;
Data = new List<string>();
}
public string Prefix;
public char OpeningTag;
public bool isOutputStarted;
public List<string> Data;
}
internal static string IndentJSON(string jsonString, int startIndent = 0, int indentSpaces = 2)
{
if (String.IsNullOrEmpty(jsonString))
return jsonString;
try
{
var jsonCache = new List<IndentJsonInfo>();
IndentJsonInfo currentItem = null;
var sbResult = new StringBuilder();
int curIndex = 0;
bool inQuotedText = false;
var chunk = new StringBuilder();
var saveChunk = new Action(() =>
{
if (chunk.Length == 0)
return;
if (currentItem == null)
throw new Exception("Invalid JSON: No container.");
currentItem.Data.Add(chunk.ToString());
chunk = new StringBuilder();
});
while (curIndex < jsonString.Length)
{
var cChar = jsonString[curIndex];
if (inQuotedText)
{
// Get the rest of quoted text.
chunk.Append(cChar);
// Determine if the quote is escaped.
bool isEscaped = false;
var excapeIndex = curIndex;
while (excapeIndex > 0 && jsonString[--excapeIndex] == '\\') isEscaped = !isEscaped;
if (cChar == '"' && !isEscaped)
inQuotedText = false;
}
else if (Char.IsWhiteSpace(cChar))
{
// Ignore all whitespace outside of quotes.
}
else
{
// Outside of Quotes.
switch (cChar)
{
case '"':
chunk.Append(cChar);
inQuotedText = true;
break;
case ',':
chunk.Append(cChar);
saveChunk();
break;
case '{':
case '[':
currentItem = new IndentJsonInfo(chunk.ToString(), cChar);
jsonCache.Add(currentItem);
chunk = new StringBuilder();
break;
case '}':
case ']':
saveChunk();
for (int i = 0; i < jsonCache.Count; i++)
{
var item = jsonCache[i];
var isLast = i == jsonCache.Count - 1;
if (!isLast)
{
if (!item.isOutputStarted)
{
sbResult.AppendLine(
"".PadLeft((startIndent + i) * indentSpaces) +
item.Prefix + item.OpeningTag);
item.isOutputStarted = true;
}
var newIndentString = "".PadLeft((startIndent + i + 1) * indentSpaces);
foreach (var listItem in item.Data)
{
sbResult.AppendLine(newIndentString + listItem);
}
item.Data = new List<string>();
}
else // If Last
{
if (!(
(item.OpeningTag == '{' && cChar == '}') ||
(item.OpeningTag == '[' && cChar == ']')
))
{
throw new Exception("Invalid JSON: Container Mismatch, Open '" + item.OpeningTag + "', Close '" + cChar + "'.");
}
string closing = null;
if (item.isOutputStarted)
{
var newIndentString = "".PadLeft((startIndent + i + 1) * indentSpaces);
foreach (var listItem in item.Data)
{
sbResult.AppendLine(newIndentString + listItem);
}
closing = cChar.ToString();
}
else
{
closing =
item.Prefix + item.OpeningTag +
String.Join("", currentItem.Data.ToArray()) +
cChar;
}
jsonCache.RemoveAt(i);
currentItem = (jsonCache.Count > 0) ? jsonCache[jsonCache.Count - 1] : null;
chunk.Append(closing);
}
}
break;
default:
chunk.Append(cChar);
break;
}
}
curIndex++;
}
if (inQuotedText)
throw new Exception("Invalid JSON: Incomplete Quote");
else if (jsonCache.Count != 0)
throw new Exception("Invalid JSON: Incomplete Structure");
else
{
if (chunk.Length > 0)
sbResult.AppendLine("".PadLeft(startIndent * indentSpaces) + chunk);
var result = sbResult.ToString();
return result;
}
}
catch (Exception ex)
{
throw; // Comment out to return unformatted text if the format failed.
// Invalid JSON, skip the formatting.
return jsonString;
}
}
The function allows you to specify a starting point for the indentation because I use this as part of a process that assembles very large JSON formatted backup files.

Related

Split string in square brackets from Google translator

I am receiving a data from a Google Language Translator service and need help splitting the data.
void Start()
{
translateText("Hello, This is a test!", "en", "fr");
}
void translateText(string text, string fromLanguage, string toLanguage)
{
string url = "https://translate.googleapis.com/translate_a/single?client=gtx&sl=" + fromLanguage + "&tl=" + toLanguage + "&dt=t&q=" + Uri.EscapeUriString(text);
StartCoroutine(startTranslator(url));
}
IEnumerator startTranslator(string url)
{
UnityWebRequest www = UnityWebRequest.Get(url);
yield return www.Send();
Debug.Log("Raw string Received: " + www.downloadHandler.text);
LanguageResult tempResult = decodeResult(www.downloadHandler.text);
Debug.Log("Original Text: " + tempResult.originalText);
Debug.Log("Translated Text: " + tempResult.translatedText);
Debug.Log("LanguageIso: " + tempResult.languageIso);
yield return null;
}
LanguageResult decodeResult(string result)
{
char[] delims = { '[', '\"', ']', ',' };
string[] arr = result.Split(delims, StringSplitOptions.RemoveEmptyEntries);
LanguageResult tempLang = null;
if (arr.Length >= 4)
{
tempLang = new LanguageResult();
tempLang.translatedText = arr[0];
tempLang.originalText = arr[1];
tempLang.unknowValue = arr[2];
tempLang.languageIso = arr[3];
}
return tempLang;
}
public class LanguageResult
{
public string translatedText;
public string originalText;
public string unknowValue;
public string languageIso;
}
then calling it with translateText("Hello, This is a test!", "en", "fr"); from the Start() function which converts the English sentence to French with ISO 639-1 Code.
The received data looks like this:
[[["Bonjour, Ceci est un test!","Hello, This is a test!",,,0]],,"en"]
I want to split it like this:
Bonjour, Ceci est un test!
Hello, This is a test!
0
en
and put them into a string array in order.
I currently use this:
char[] delims = { '[', '\"', ']', ',' };
string[] arr = result.Split(delims, StringSplitOptions.RemoveEmptyEntries);
This works if there is no comma in the received string. If there is a comma, the splitted values are messed up. What's the best way of splitting this?
EDIT:
With Blorgbeard's solution, the final working code is as below. Hopefully, this will help somebody else. This shouldn't be used for commercial purposes but for personal or school project.
void Start()
{
//translateText("Hello, This is \" / \\ a test !", "en", "fr");
//translateText("Hello, This is , \\ \" a test !", "en", "fr");
translateText("Hello, This is a test!", "en", "fr");
}
void translateText(string text, string fromLanguage, string toLanguage)
{
string url = "https://translate.googleapis.com/translate_a/single?client=gtx&sl=" + fromLanguage + "&tl=" + toLanguage + "&dt=t&q=" + Uri.EscapeUriString(text);
StartCoroutine(startTranslator(url));
}
IEnumerator startTranslator(string url)
{
UnityWebRequest www = UnityWebRequest.Get(url);
yield return www.Send();
Debug.Log("Raw string Received: " + www.downloadHandler.text);
LanguageResult tempResult = decodeResult(www.downloadHandler.text);
displayResult(tempResult);
yield return null;
}
void displayResult(LanguageResult translationResult)
{
Debug.Log("Original Text: " + translationResult.originalText);
Debug.Log("Translated Text: " + translationResult.translatedText);
Debug.Log("LanguageIso: " + translationResult.languageIso);
}
LanguageResult decodeResult(string result)
{
string[] arr = Decode(result);
LanguageResult tempLang = null;
if (arr.Length >= 4)
{
tempLang = new LanguageResult();
tempLang.translatedText = arr[0];
tempLang.originalText = arr[1];
tempLang.unknowValue = arr[2];
tempLang.languageIso = arr[3];
}
return tempLang;
}
public class LanguageResult
{
public string translatedText;
public string originalText;
public string unknowValue;
public string languageIso;
}
private string[] Decode(string input)
{
List<string> finalResult = new List<string>();
bool inToken = false;
bool inString = false;
bool escaped = false;
var seps = ",[]\"".ToArray();
var current = "";
foreach (var chr in input)
{
if (!inString && chr == '"')
{
current = "";
inString = true;
continue;
}
if (inString && !escaped && chr == '"')
{
finalResult.Add(current);
current = "";
inString = false;
continue;
}
if (inString && !escaped && chr == '\\')
{
escaped = true;
continue;
}
if (inString && (chr != '"' || escaped))
{
escaped = false;
current += chr;
continue;
}
if (inToken && seps.Contains(chr))
{
finalResult.Add(current);
current = "";
inToken = false;
continue;
}
if (!inString && chr == '"')
{
inString = true;
current = "";
continue;
}
if (!inToken && !seps.Contains(chr))
{
inToken = true;
current = "";
}
current += chr;
}
return finalResult.ToArray();
}
You could code up a simple parser yourself. Here's one I threw together (could use some cleaning up, but demonstrates the idea):
private static IEnumerable<string> Parse(string input) {
bool inToken = false;
bool inString = false;
bool escaped = false;
var seps = ",[]\"".ToArray();
var current = "";
foreach (var chr in input) {
if (!inString && chr == '"') {
current = "";
inString = true;
continue;
}
if (inString && !escaped && chr == '"') {
yield return current;
current = "";
inString = false;
continue;
}
if (inString && !escaped && chr == '\\') {
escaped = true;
continue;
}
if (inString && (chr != '"' || escaped)) {
escaped = false;
current += chr;
continue;
}
if (inToken && seps.Contains(chr)) {
yield return current;
current = "";
inToken = false;
continue;
}
if (!inString && chr == '"') {
inString = true;
current = "";
continue;
}
if (!inToken && !seps.Contains(chr)) {
inToken = true;
current = "";
}
current += chr;
}
}
Here's a jsfiddle demo.
Using Regex.Split you could do something like this for example:
using System;
using System.Text.RegularExpressions;
public class Example
{
public static void Main()
{
var input ="[[[\"Bonjour, Ceci est un test!\",\"Hello, This is a test!\",,,0]],,\"en\"]";
var parse = Regex.Split(input, "\\[|\\]|[^a-zA-Z ],|\",\"|\"|\"");
foreach(var item in parse) {
bool result = !String.IsNullOrEmpty(item) && (Char.IsLetter(item[0]) || Char.IsDigit(item[0]));
if (result) {
Console.WriteLine(item);
}
}
}
}
Output:
Bonjour, Ceci est un test!
Hello, This is a test!
0
en
If you want everything that was split you can simply remove the bool check for alphacharacters.
Here is a crazy idea - split by " and then by the rest (but won't work if there is " between the "'s)
var s = #"[[[""Bonjour, Ceci est un test!"",""Hello, This is a test!"",,,0]],,""en""]";
var a = s.Split('"').Select((x, i) => (i & 1) > 0 ? new[] { x } : x.Split("[],".ToArray(),
StringSplitOptions.RemoveEmptyEntries)).SelectMany(x => x).ToArray();
Debug.Print(string.Join("|", a)); // "Bonjour, Ceci est un test!|Hello, This is a test!|0|en"
You can try regex for splitting. I tested with the sample you provided. It results like this.
var str="[[[\"Bonjour, Ceci est un test!\",\"Hello, This is a test!\",,,0]],,\"en\"]";
var splitted=Regex.Split(str,#"\[|\]|\,");
foreach(var split in splitted){
Console.WriteLine(split );
}
"Bonjour Ceci est un test!"
"Hello This is a test!"
0
"en"

Fastest way to create json in C#

I have a sample of data which is given below.
{
"ditems": [
{
"type": "ditem",
"name": "webmet.com",
"ditem": 0,
"links": [
"www.abc.com/a",
"www.sempo.org/"
]
},
{
"type": "ditem",
"name": "webmet.com/who-we-are/careers",
"ditem": 2,
"links": [
"http://www.tele12.com/about",
"http://tele12.com/life-at-teletech-en-US/about-teletech/"
]
}
],
"themes": [
{
"type": "theme",
"name": "http://searchm.com/isr/agenda",
"description": "",
"slug": "http://searchm.com/isr/agenda-2"
},
{
"type": "theme",
"name": "http://www.sempo.org/",
"description": "",
"slug": "http://www.sempo.org/-2"
}
]
}
Here is my code
var InternalURLList = dtInternalURL.AsEnumerable().Select(c => c.Field<string>("URL")).Distinct().ToList();
StringBuilder sb = new StringBuilder();
StringBuilder ThemeSb = new StringBuilder();
sb.Append("{\"ditems\":[");
if (InternalURLList.Count > 0)
{
for (int i = 0; i < InternalURLList.Count; i++)
{
var ExternalDomainList = GetExternalDomain(Domain_ID, InternalURLList[i]).AsEnumerable().Select(c => c.Field<string>("ExternalDomain")).Distinct().ToList();
sb.Append("{\"type\":\"ditem\",");
sb.Append("\"name\":");
sb.Append("\"" + InternalURLList[i] + "\",");
sb.Append("\"ditem\":" + i + ",");
sb.Append("\"links\":[");
if (ExternalDomainList.Count > 0)
{
for (int j = 0; j < ExternalDomainList.Count; j++)
{
sb.Append("\"" + ExternalDomainList[j] + "\"");
sb.Append((j != ExternalDomainList.Count - 1) ? "," : "]") ;
}
}
else
{
sb.Append("]");
}
sb.Append("}");
sb.Append((i != InternalURLList.Count - 1)?",":"]");
}
}
else
{
sb.Append("]");
sb.Append(",");
}
DataTable dtDistinctDomain = GetDistinctExtDomain(Domain_ID);
var ExternalDomain = dtDistinctDomain.AsEnumerable().Select(c => c.Field<string>("ExternalDomain")).Distinct().ToList();
ThemeSb.Append(",\"themes\":[");
for (int i = 0; i < ExternalDomain.Count; i++)
{
// For theme
ThemeSb.Append("{\"type\":\"theme\",");
ThemeSb.Append("\"name\":\"" + ExternalDomain[i] + "\",");
ThemeSb.Append("\"description\":\"\",");
ThemeSb.Append("\"slug\":\"" + ExternalDomain[i] + "-2" + "\"}");
ThemeSb.Append((i != ExternalDomain.Count - 1)?",":"]");
}
ThemeSb.Append("}");
string ConceptMapTheme = ThemeSb.ToString() ;
string ConceptMapJSON = sb.ToString()+ThemeSb.ToString();
return ConceptMapJSON;
I am trying to make json in C# using StringBuilder. The for loop is taking too much time to generate the json. The table contains more than 2,00,000 records. How to create json in a fastest way using C#?
You can add the Newtonsoft.Json nuget package in your project.
Then you just need to call the serializer :
var jsonString = JsonConvert.SerializeObject(objectToSerialize);

Writing the most simple newick parser for Unity3d (c# or Actionscript)

I am trying to figure out how to read Newick files for many animal species and i haven't been able to find a "logical method / process" to sort the Newick string in a simple programming language. I can read C# and AS and JS and GLSL and HLSL.
I can't find any simple resources, and the wiki article doesn't even talk about recursion. A pseudocode of how to parse newick would be so great and i can't find one.
Does anyone know the fastest way to read a newick file in Unity3d? Can you help to set me on the right track for a logical process to sort through the newick code, i.e:
(A,B,(C,D));
the branch lengh number is not important for the moment.
target project file:
(
(
(
(
(
(
Falco_rusticolus:0.846772,
Falco_jugger:0.846772
):0.507212,
(
Falco_cherrug:0.802297,
Falco_subniger:0.802297
):0.551687
):0.407358,
Falco_biarmicus:1.761342
):1.917030,
(
Falco_peregrinus:0.411352,
Falco_pelegrinoides:0.411352
):3.267020
):2.244290,
Falco_mexicanus:5.922662
):1.768128,
Falco_columbarius:7.69079
)
Implementing a parser if you have no background in formal grammars might be tough. So the easiest approach seems to be to use a parser generator, such as ANTLR, and then you only need to familiarize yourself with the grammar notation. You can generate a parser written in C# from a grammar.
Luckily you can find a newick grammar online: here.
UPDATE:
And if you did the above, then you'll get something like the following:
public class Branch
{
public double Length { get; set; }
public List<Branch> SubBranches { get; set; } = new List<Branch>();
}
public class Leaf : Branch
{
public string Name { get; set; }
}
public class Parser
{
private int currentPosition;
private string input;
public Parser(string text)
{
input = new string(text.Where(c=>!char.IsWhiteSpace(c)).ToArray());
currentPosition = 0;
}
public Branch ParseTree()
{
return new Branch { SubBranches = ParseBranchSet() };
}
private List<Branch> ParseBranchSet()
{
var ret = new List<Branch>();
ret.Add(ParseBranch());
while (PeekCharacter() == ',')
{
currentPosition++; // ','
ret.Add(ParseBranch());
}
return ret;
}
private Branch ParseBranch()
{
var tree = ParseSubTree();
currentPosition++; // ':'
tree.Length = ParseDouble();
return tree;
}
private Branch ParseSubTree()
{
if (char.IsLetter(PeekCharacter()))
{
return new Leaf { Name = ParseIdentifier() };
}
currentPosition++; // '('
var branches = ParseBranchSet();
currentPosition++; // ')'
return new Branch { SubBranches = branches };
}
private string ParseIdentifier()
{
var identifer = "";
char c;
while ((c = PeekCharacter()) != 0 && (char.IsLetter(c) || c == '_'))
{
identifer += c;
currentPosition++;
}
return identifer;
}
private double ParseDouble()
{
var num = "";
char c;
while((c = PeekCharacter()) != 0 && (char.IsDigit(c) || c == '.'))
{
num += c;
currentPosition++;
}
return double.Parse(num, CultureInfo.InvariantCulture);
}
private char PeekCharacter()
{
if (currentPosition >= input.Length-1)
{
return (char)0;
}
return input[currentPosition + 1];
}
}
Which can be used like this:
var tree = new Parser("((A:1, B:2):3, C:4)").ParseTree();
BTW the above parser implements the following grammar without any kind of error handling:
Tree -> "(" BranchSet ")"
BranchSet -> Branch ("," Branch)*
Branch -> Subtree ":" NUM
Subtree -> IDENTIFIER | "(" BranchSet ")"
Hope you're interested in converting Newick into JSON/Regular object, I think I was able to find a solution.
A quick google gave me links to the implementation on JS:
https://www.npmjs.com/package/biojs-io-newick
https://github.com/daviddao/biojs-io-newick
And, it wasn't hard for me to port JS code into AS3:
// The very funciton of converting Newick
function convertNewickToJSON(source:String):Object
{
var ancestors:Array = [];
var tree:Object = {};
var tokens:Array = source.split(/\s*(;|\(|\)|,|:)\s*/);
var subtree:Object;
for (var i = 0; i < tokens.length; i++)
{
var token:String = tokens[i];
switch (token)
{
case '(': // new children
subtree = {};
tree.children = [subtree];
ancestors.push(tree);
tree = subtree;
break;
case ',': // another branch
subtree = {};
ancestors[ancestors.length-1].children.push(subtree);
tree = subtree;
break;
case ')': // optional name next
tree = ancestors.pop();
break;
case ':': // optional length next
break;
default:
var x = tokens[i-1];
if (x == ')' || x == '(' || x == ',')
{
tree.name = token;
} else if (x == ':')
{
tree.branch_length = parseFloat(token);
}
}
}
return tree;
};
// Util function for parsing an object into a string
function objectToStr(obj:Object, paramsSeparator:String = "", isNeedUseSeparatorForChild:Boolean = false):String
{
var str:String = "";
if (isSimpleType(obj))
{
str = String(obj);
}else
{
var childSeparator:String = "";
if (isNeedUseSeparatorForChild)
{
childSeparator = paramsSeparator;
}
for (var propName:String in obj)
{
if (str == "")
{
str += "{ ";
}else
{
str += ", ";
}
str += propName + ": " + objectToStr(obj[propName], childSeparator) + paramsSeparator;
}
str += " }";
}
return str;
}
// One more util function
function isSimpleType(obj:Object):Boolean
{
var isSimple:Boolean = false;
if (typeof(obj) == "string" || typeof(obj) == "number" || typeof(obj) == "boolean")
{
isSimple = true;
}
return isSimple;
}
var tempNewickSource:String = "((((((Falco_rusticolus:0.846772,Falco_jugger:0.846772):0.507212,(Falco_cherrug:0.802297,Falco_subniger:0.802297):0.551687):0.407358,Falco_biarmicus:1.761342):1.917030,(Falco_peregrinus:0.411352,Falco_pelegrinoides:0.411352):3.267020):2.244290,Falco_mexicanus:5.922662):1.768128,Falco_columbarius:7.69079)";
var tempNewickJSON:Object = this.convertNewickToJSON(tempNewickSource);
var tempNewickJSONText:String = objectToStr(tempNewickJSON);
trace(tempNewickJSONText);
The code above gives the next trace:
{ name: , children: { 0: { name: , children: { 0: { name: , children: { 0: { name: , children: { 0: { name: , children: { 0: { name: , children: { 0: { name: Falco_rusticolus, branch_length: 0.846772 }, 1: { name: Falco_jugger, branch_length: 0.846772 } }, branch_length: 0.507212 }, 1: { name: , children: { 0: { name: Falco_cherrug, branch_length: 0.802297 }, 1: { name: Falco_subniger, branch_length: 0.802297 } }, branch_length: 0.551687 } }, branch_length: 0.407358 }, 1: { name: Falco_biarmicus, branch_length: 1.761342 } }, branch_length: 1.91703 }, 1: { name: , children: { 0: { name: Falco_peregrinus, branch_length: 0.411352 }, 1: { name: Falco_pelegrinoides, branch_length: 0.411352 } }, branch_length: 3.26702 } }, branch_length: 2.24429 }, 1: { name: Falco_mexicanus, branch_length: 5.922662 } }, branch_length: 1.768128 }, 1: { name: Falco_columbarius, branch_length: 7.69079 } } }
So, this approach gives a way to work with a Newick format as with JSON.
According to the title, you're interested not only in C#, but in AS3 implementation too (I'm not sure that you'll be able to use it in C# right "out-of-the-box", but maybe you will be able to port it to C#).

Advanced JSON serialization formatting options

I have some data in form of objects, inside a list object. Now I want to serialize this data to JSON. For this I'm (currently) using JSON.NET. My problem is that with
JsonConvert.SerializeObject(list, ...)
I seem to only have the option to either indent the whole thing, aka every key/value, or not indent at all.
{"Variable1":1,"Variable2":"2"},{"Variable1":1,"Variable2":"2"},...
or
{
"Variable1": 1,
"Variable2": "2"
},
{
"Variable1": 1,
"Variable2": "2"
},
...
I'd like to get this:
{ "Variable1": 1, "Variable2": "2" },
{ "Variable1": 1, "Variable2": "2" },
but without having to explicitly writing every key/value myself (JsonTextWriter or manually). I just want to pass the list and get the above. Is this somehow possible? At the moment I'm serializing every object separate, by going through the list, and running some replaces, regex replaces, and similar, to get the desired output, depending on the input list. Is there an easier way to do this, without writing your own serialization method?
hello i found something that my help you to start your special formatter
i translated it from javascript to c# from here: https://github.com/umbrae/jsonlintdotcom/blob/master/c/js/jsl.format.js
it add whitespaces to json string without them.
i think you can modify this formatter to your own needs.
private static string PrettyPrinter_repeat(string s, int count) {
string ns = "";
for(int i = 0; i < count; i++) ns += s;
return ns;
}
public static string PrettyPrinter(string json)
{
int i = 0,
il = 0;
string tab = " ",
newJson = "";
int indentLevel = 0;
bool inString = false;
char currentChar = default(char);
for (i = 0, il = json.Length; i < il; i += 1)
{
currentChar = json[i];
switch (currentChar) {
case '{':
case '[':
if (!inString) {
newJson += currentChar + "\n" + PrettyPrinter_repeat(tab, indentLevel + 1);
indentLevel += 1;
} else {
newJson += currentChar;
}
break;
case '}':
case ']':
if (!inString) {
indentLevel -= 1;
newJson += "\n" + PrettyPrinter_repeat(tab, indentLevel) + currentChar;
} else {
newJson += currentChar;
}
break;
case ',':
if (!inString) {
newJson += ",\n" + PrettyPrinter_repeat(tab, indentLevel);
} else {
newJson += currentChar;
}
break;
case ':':
if (!inString) {
newJson += ": ";
} else {
newJson += currentChar;
}
break;
case ' ':
case '\n':
case '\t':
if (inString) {
newJson += currentChar;
}
break;
case '"':
if (i > 0 && json[i - 1] != '\\') {
inString = !inString;
}
newJson += currentChar;
break;
default:
newJson += currentChar;
break;
}
}
return newJson;
}

Logic to decrease character values

I am working on a logic that decreases the value of an alphanumeric List<char>. For example, A10 becomes A9, BBA becomes BAZ, 123 becomes 122. And yes, if the value entered is the last one(like A or 0), then I should return -
An additional overhead is that there is a List<char> variable which is maintained by the user. It has characters which are to be skipped. For example, if the list contains A in it, the value GHB should become GGZ and not GHA.
The base of this logic is a very simple usage of decreasing the char but with these conditions, I am finding it very difficult.
My project is in Silverlight, the language is C#. Following is my code that I have been trying to do in the 3 methods:
List<char> lstGetDecrName(List<char> lstVal)//entry point of the value that returns decreased value
{
List<char> lstTmp = lstVal;
subCheckEmpty(ref lstTmp);
switch (lstTmp.Count)
{
case 0:
lstTmp.Add('-');
return lstTmp;
case 1:
if (lstTmp[0] == '-')
{
return lstTmp;
}
break;
case 2:
if (lstTmp[1] == '0')
{
if (lstTmp[0] == '1')
{
lstTmp.Clear();
lstTmp.Add('9');
return lstTmp;
}
if (lstTmp[0] == 'A')
{
lstTmp.Clear();
lstTmp.Add('-');
return lstTmp;
}
}
if (lstTmp[1] == 'A')
{
if (lstTmp[0] == 'A')
{
lstTmp.Clear();
lstTmp.Add('Z');
return lstTmp;
}
}
break;
}
return lstGetDecrValue(lstTmp,lstVal);
}
List<char> lstGetDecrValue(List<char> lstTmp,List<char> lstVal)
{
List<char> lstValue = new List<char>();
switch (lstTmp.Last())
{
case 'A':
lstValue = lstGetDecrTemp('Z', lstTmp, lstVal);
break;
case 'a':
lstValue = lstGetDecrTemp('z', lstTmp, lstVal);
break;
case '0':
lstValue = lstGetDecrTemp('9', lstTmp, lstVal);
break;
default:
char tmp = (char)(lstTmp.Last() - 1);
lstTmp.RemoveAt(lstTmp.Count - 1);
lstTmp.Add(tmp);
lstValue = lstTmp;
break;
}
return lstValue;
}
List<char> lstGetDecrTemp(char chrTemp, List<char> lstTmp, List<char> lstVal)//shifting places eg unit to ten,etc.
{
if (lstTmp.Count == 1)
{
lstTmp.Clear();
lstTmp.Add('-');
return lstTmp;
}
lstTmp.RemoveAt(lstTmp.Count - 1);
lstVal = lstGetDecrName(lstTmp);
lstVal.Insert(lstVal.Count, chrTemp);
return lstVal;
}
I seriously need help for this. Please help me out crack through this.
The problem you are trying to solve is actually how to decrement discreet sections of a sequence of characters, each with it's own counting system, where each section is separated by a change between Alpha and Numeric. The rest of the problem is easy once you identify this.
The skipping of unwanted characters is simply a matter of repeating the decrement if you get an unwanted character in the result.
One difficultly is the ambiguous definition of the sequences. e.g. what to do when you get down to say A00, what is next? "A" or "-". For the sake of argument I am assuming a practical implementation based loosely on Excel cell names (i.e. each section operates independently of the others).
The code below does 95% of what you wanted, however there is a bug in the exclusions code. e.g. "ABB" becomes "AAY". I feel the exclusions need to be applied at a higher level (e.g. repeat decrement until no character is in the exclusions list), but I don't have time to finish it now. Also it is resulting in a blank string when it counts down to nothing, rather than the "-" you wanted, but that is trivial to add at the end of the process.
Part 1 (divide the problem into sections):
public static string DecreaseName( string name, string exclusions )
{
if (string.IsNullOrEmpty(name))
{
return name;
}
// Split the problem into sections (reverse order)
List<StringBuilder> sections = new List<StringBuilder>();
StringBuilder result = new StringBuilder(name.Length);
bool isNumeric = char.IsNumber(name[0]);
StringBuilder sb = new StringBuilder();
sections.Add(sb);
foreach (char c in name)
{
// If we change between alpha and number, start new string.
if (char.IsNumber(c) != isNumeric)
{
isNumeric = char.IsNumber(c);
sb = new StringBuilder();
sections.Insert(0, sb);
}
sb.Append(c);
}
// Now process each section
bool cascadeToNext = true;
foreach (StringBuilder section in sections)
{
if (cascadeToNext)
{
result.Insert(0, DecrementString(section, exclusions, out cascadeToNext));
}
else
{
result.Insert(0, section);
}
}
return result.ToString().Replace(" ", "");
}
Part2 (decrement a given string):
private static string DecrementString(StringBuilder section, string exclusions, out bool cascadeToNext)
{
bool exclusionsExist = false;
do
{
exclusionsExist = false;
cascadeToNext = true;
// Process characters in reverse
for (int i = section.Length - 1; i >= 0 && cascadeToNext; i--)
{
char c = section[i];
switch (c)
{
case 'A':
c = (i > 0) ? 'Z' : ' ';
cascadeToNext = (i > 0);
break;
case 'a':
c = (i > 0) ? 'z' : ' ';
cascadeToNext = (i > 0);
break;
case '0':
c = (i > 0) ? '9' : ' ';
cascadeToNext = (i > 0);
break;
case ' ':
cascadeToNext = false;
break;
default:
c = (char)(((int)c) - 1);
if (i == 0 && c == '0')
{
c = ' ';
}
cascadeToNext = false;
break;
}
section[i] = c;
if (exclusions.Contains(c.ToString()))
{
exclusionsExist = true;
}
}
} while (exclusionsExist);
return section.ToString();
}
The dividing can of course be done more efficiently, just passing start and end indexes to the DecrementString, but this is easier to write & follow and not much slower in practical terms.
do a check if its a number if so then do a minus math of the number, if its a string then change it to char codes and then the char code minus 1
I couldn't stop thinking about this yesterday, so here's an idea. Note, this is just pseudo-code, and not tested, but I think the idea is valid and should work (with a few modifications).
The main point is to define your "alphabet" directly, and specify which characters in it are illegal and should be skipped, then use a list or array of positions in this alphabet to define the word you start with.
I can't spend any more time on this right now, but please let me know if you decide to use it and get it to work!
string[] alphabet = {a, b, c, d, e};
string[] illegal = {c, d};
public string ReduceString(string s){
// Create a list of the alphabet-positions for each letter:
int[] positionList = s.getCharsAsPosNrsInAlphabet();
int[] reducedPositionList = ReduceChar(positionList, positionList.length);
string result = "";
foreach(int pos in reducedPositionList){
result += alphabet[pos];
}
return result;
}
public string ReduceChar(string[] positionList, posToReduce){
int reducedCharPosition = ReduceToNextLegalChar(positionList[posToReduce]);
// put reduced char back in place:
positionList[posToReduce] = reducedCharPosition;
if(reducedCharPosition < 0){
if(posToReduce <= 0){
// Reached the end, reduced everything, return empty array!:
return new string[]();
}
// move to back of alphabet again (ie, like the 9 in "11 - 2 = 09"):
reducedCharPosition += alphabet.length;
// Recur and reduce next position (ie, like the 0 in "11 - 2 = 09"):
return ReduceChar(positionList, posToReduce-1);
}
return positionList;
}
public int ReduceToNextLegalChar(int pos){
int nextPos = pos--;
return (isLegalChar(nextPos) ? nextPos : ReduceToNextLegalChar(nextPos));
}
public boolean IsLegalChar(int pos){
return (! illegal.contains(alphabet[pos]));
}
enter code here
Without writing all your code for you, here's a suggestion as to how you can break this down:
char DecrementAlphaNumericChar(char input, out bool hadToWrap)
{
if (input == 'A')
{
hadToWrap = true;
return 'Z';
}
else if (input == '0')
{
hadToWrap = true;
return '9';
}
else if ((input > 'A' && input <= 'Z') || (input > '0' && input <= '9'))
{
hadToWrap = false;
return (char)((int)input - 1);
}
throw new ArgumentException(
"Characters must be digits or capital letters",
"input");
}
char DecrementAvoidingProhibited(
char input, List<char> prohibited, out bool hadToWrap)
{
var potential = DecrementAlphaNumericChar(input, out hadToWrap);
while (prohibited.Contains(potential))
{
bool temp;
potential = DecrementAlphaNumericChar(potential, out temp);
if (potential == input)
{
throw new ArgumentException(
"A whole class of characters was prohibited",
"prohibited");
}
hadToWrap |= temp;
}
return potential;
}
string DecrementString(string input, List<char> prohibited)
{
char[] chrs = input.ToCharArray();
for (int i = chrs.Length - 1; i >= 0; i--)
{
bool wrapped;
chrs[i] = DecrementAvoidingProhibited(
chrs[i], prohibited, out wrapped);
if (!wrapped)
return new string(chrs);
}
return "-";
}
The only issue here is that it will reduce e.g. A10 to A09 not A9. I actually prefer this myself, but it should be simple to write a final pass that removes the extra zeroes.
For a little more performance, replace the List<char>s with Hashset<char>s, they should allow a faster Contains lookup.
I found solution to my own answer with some other workarounds.
The calling function:
MyFunction()
{
//stuff I do before
strValue = lstGetDecrName(strValue.ToList());//decrease value here
if (strValue.Contains('-'))
{
strValue = "-";
}
//stuff I do after
}
In all there are 4 functions. 2 Main functions and 2 helper functions.
List<char> lstGetDecrName(List<char> lstVal)//entry point, returns decreased value
{
if (lstVal.Contains('-'))
{
return "-".ToList();
}
List<char> lstTmp = lstVal;
subCheckEmpty(ref lstTmp);
switch (lstTmp.Count)
{
case 0:
lstTmp.Add('-');
return lstTmp;
case 1:
if (lstTmp[0] == '-')
{
return lstTmp;
}
break;
case 2:
if (lstTmp[1] == '0')
{
if (lstTmp[0] == '1')
{
lstTmp.Clear();
lstTmp.Add('9');
return lstTmp;
}
if (lstTmp[0] == 'A')
{
lstTmp.Clear();
lstTmp.Add('-');
return lstTmp;
}
}
if (lstTmp[1] == 'A')
{
if (lstTmp[0] == 'A')
{
lstTmp.Clear();
lstTmp.Add('Z');
return lstTmp;
}
}
break;
}
List<char> lstValue = new List<char>();
switch (lstTmp.Last())
{
case 'A':
lstValue = lstGetDecrTemp('Z', lstTmp, lstVal);
break;
case 'a':
lstValue = lstGetDecrTemp('z', lstTmp, lstVal);
break;
case '0':
lstValue = lstGetDecrTemp('9', lstTmp, lstVal);
break;
default:
char tmp = (char)(lstTmp.Last() - 1);
lstTmp.RemoveAt(lstTmp.Count - 1);
lstTmp.Add(tmp);
subCheckEmpty(ref lstTmp);
lstValue = lstTmp;
break;
}
lstGetDecrSkipValue(lstValue);
return lstValue;
}
List<char> lstGetDecrSkipValue(List<char> lstValue)
{
bool blnSkip = false;
foreach (char tmpChar in lstValue)
{
if (lstChars.Contains(tmpChar))
{
blnSkip = true;
break;
}
}
if (blnSkip)
{
lstValue = lstGetDecrName(lstValue);
}
return lstValue;
}
void subCheckEmpty(ref List<char> lstTmp)
{
bool blnFirst = true;
int i = -1;
foreach (char tmpChar in lstTmp)
{
if (char.IsDigit(tmpChar) && blnFirst)
{
i = tmpChar == '0' ? lstTmp.IndexOf(tmpChar) : -1;
if (tmpChar == '0')
{
i = lstTmp.IndexOf(tmpChar);
}
blnFirst = false;
}
}
if (!blnFirst && i != -1)
{
lstTmp.RemoveAt(i);
subCheckEmpty(ref lstTmp);
}
}
List<char> lstGetDecrTemp(char chrTemp, List<char> lstTmp, List<char> lstVal)//shifting places eg unit to ten,etc.
{
if (lstTmp.Count == 1)
{
lstTmp.Clear();
lstTmp.Add('-');
return lstTmp;
}
lstTmp.RemoveAt(lstTmp.Count - 1);
lstVal = lstGetDecrName(lstTmp);
lstVal.Insert(lstVal.Count, chrTemp);
subCheckEmpty(ref lstVal);
return lstVal;
}

Categories

Resources