I am working on a Razor Page Web App In Asp Core and I am using this code to generate calendars with html and java script. When I click on a day of a calendar, I am being redirected to a page. In that page I will need to retrieve some data from my Model class based on the day that was selected by the user.
Is there any way I can save the clicked date to use in my Razor Page class ?
Here is the js code:
function myFunction() {
function calendar() {
var padding = "";
var totalFeb = "";
var i = 1;
var current = new Date();
var cmonth = current.getMonth();
var day = current.getDate();
var tempMonth = month + 1; //+1;
var prevMonth = month - 1;
if (month == 1) {
if ((year % 100 !== 0) && (year % 4 === 0) || (year % 400 === 0)) {
totalFeb = 29;
} else {
totalFeb = 28;
}
}
var monthNames = ["Jan", "Feb", "March", "April", "May", "June", "July", "Aug", "Sept", "Oct", "Nov", "Dec"];
var dayNames = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thrusday", "Friday", "Saturday"];
var totalDays = ["31", "" + totalFeb + "", "31", "30", "31", "30", "31", "31", "30", "31", "30", "31"];
var tempDate = new Date(tempMonth + ' 1 ,' + year);
var tempweekday = tempDate.getDay();
var tempweekday2 = tempweekday;
var dayAmount = totalDays[month];
while (tempweekday > 0) {
padding += "<td class='premonth'></td>";
tempweekday--;
}
while (i <= dayAmount) {
if (tempweekday2 > 6) {
tempweekday2 = 0;
padding += "</tr><tr>";
}
if (i == day && month == cmonth) {
padding += "<td class='currentday' onclick='redirect();' onMouseOver='this.style.background=\"#00FF00\"; this.style.color=\"#FFFFFF\"' onMouseOut='this.style.background=\"#FFFFFF\"; this.style.color=\"#00FF00\"'>" + i + "</td>";
} else {
padding += "<td class='currentmonth' onclick='redirect();' onMouseOver='this.style.background=\"#00FF00\"' onMouseOut='this.style.background=\"#FFFFFF\"'>" + i + "</td>";
}
tempweekday2++;
i++;
}
var calendarTable = "<table class='calendar'> <tr class='currentmonth'><th colspan='7'>" + monthNames[month] + " " + year + "</th></tr>";
calendarTable += "<tr class='weekdays'> <td>Sun</td> <td>Mon</td> <td>Tues</td> <td>Wed</td> <td>Thurs</td> <td>Fri</td> <td>Sat</td> </tr>";
calendarTable += "<tr>";/ calendarTable += padding;
calendarTable += "</tr></table>";
document.getElementById("calendar").innerHTML += calendarTable;
}
function go12() {
for (i = 6; i < 12; i++) {
calendar(i);
}
}
if (window.addEventListener) {
window.addEventListener('load', go12, false);
} else if (window.attachEvent) {
window.attachEvent('onload', go12);
}
})();
function redirect() {
window.location.replace("./MyPage");
}
In my Razor page:
public class MyPageyModel : PageModel
{
private readonly ILogger<MyPageModel> _logger;
public PrivacyModel(ILogger<MyPageModel> logger)
{
_logger = logger;
}
public void OnGet()
{
//somewhere in this class I need a DateTime variable that represents the day that the user clicked on the calendar
}
}
}
EDIT
Based on #Wowo Ot response, I tried something like this to send the data to my class using OnPostMethod:
function redirect() {
$.ajax({
type: 'POST',
url: './MyPage',
dataType: 'string',
data: { day: $("#DAY").val() },
success: function (result) {
return true;
},
error: function (ex) {
return false;
}
});
}
And in my Razor Page:
public string OnPostSetDate(int day)
{
try
{
Debug.WriteLine(day);
return "OK";
}
catch (Exception)
{
return "Error";
}
}
But now I get an error: "Failed to load resource: the server responded with a status of 500 () [https://localhost:44350/MyPage]"
try this
in your controller
[HttpPost]
public string setDatePost(int day, int month, int year)
{
try
{
DateTime theDate = new DateTime(year, month, day);
// your code to save Date
return "OK";
}
catch (Exception)
{
return "Error";
}
}
in your page script
<script type="text/javascript">
$(document).ready(function () {
$("#CLK").click(function () { // this is your button for selecting the day
$.ajax({
type: 'POST',
url: '#Url.Action("setDatePost")', // the name of your action (function) in controller
dataType: 'string',
// these field represent your selection of day, month and year
data: { day: $("#DAY").val(),month: $("#MONTH").val(), year: $("#YEAR").val() },
success: function (result) {
return true;
},
error: function (ex) {
return false;
}
});
return false;
})
});
</script>
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#).
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.