Repository navigation
Day 12
In Day 11, our C# server and JavaScript passed one single block of plain text back and forth (text/plain). That worked great for a single notepad!
However, what if our C# server wants to send a structured object to JavaScript—for example, a Student Profile that has a name, age, exam score, membership status, join date, and a list of hobbies?
If we sent all of that as one plain text string, JavaScript would have to guess where the name ends, where the score begins, and whether "true" is text or a boolean!
To send structured data between a server and a browser, the entire web uses a universal text format called JSON (JavaScript Object Notation).
Today, we will:
-
Learn what JSON looks like and explore every data type JSON supports (strings, dates, numbers, booleans, arrays, and
null). -
Write structured, indented JSON in C# using a multiline
$@"..."string (with""double-quote escaping). -
Protect JSON strings from breaking when text contains quotes (
") usingWebApp.JavaScriptStringEncode(). -
Serve our JSON from an API route (
/api/student) withResponse.ContentType = "application/json". -
Read the JSON in JavaScript on our Home page (
/) usingfetch('/api/student')andresponse.json()!
Even though JSON sounds technical, here is the secret: JSON is still just plain text! You could type it in Notepad.
What makes JSON special is its strict, predictable shape that both C# and JavaScript understand instantly.
Let's look at a nicely indented JSON object representing a Student Profile:
{
"name": "Alice",
"age": 12,
"score": 95.5,
"isMember": true,
"joinDate": "2026-10-27 14:30:00",
"nickname": null,
"hobbies": [
"drawing",
"football"
]
}-
Curly braces
{ ... }wrap the entire Object. - Inside
{ ... }, data is written as"key": valuepairs, separated by commas (,). Notice that every key name must be wrapped in double quotes ("name","age"). - Look at the different kinds of values on the right side of the colon (
:):
| JSON Key | JSON Value | Data Type | Formatting Rule in JSON |
|---|---|---|---|
"name" |
"Alice" |
String (Text) | Always wrapped in double quotes ("..."). |
"age" |
12 |
Whole Number (int) |
Written directly with no quotes. |
"score" |
95.5 |
Decimal Number (double) |
Written directly with no quotes. |
"isMember" |
true |
Boolean (bool) |
Always lowercase true or false (no quotes!). |
"joinDate" |
"2026-10-27 14:30:00" |
Date / Time | JSON has no special date symbol, so dates are written as strings inside double quotes ("..."). |
"nickname" |
null |
Null (Empty / None) | Always lowercase null (no quotes!). |
"hobbies" |
["drawing", "football"] |
Array (List) | Wrapped in square brackets [ ... ], with commas between items. |
How do we write that exact indented JSON string inside our C# code?
Remember from Day 2 that when we use a multiline verbatim string (@"..." or $@"...") in C#, the string itself starts and ends with a double quote (").
Because JSON requires real double quotes around every key ("name") and every string value ("Alice"), how do we write a double quote inside a C# $@"..." string without ending the string early?
C# Rule — Doubling Quotes (
"") Inside$@"...": Inside a@"..."or$@"..."string, typing two double quotes right next to each other ("") tells C# to output one single literal double quote (")!
And one more rule we saw in Day 10:
C# Rule — Doubling Curly Braces (
{{and}}) Inside$@"...": Because$@"..."uses single{variable}braces to inject C# variables, we write{{at the very top of our JSON and}}at the very bottom so C# outputs literal{and}characters for our JSON object!
Let's see how clean and structured this looks in C#:
string name = "Alice";
int age = 12;
double score = 95.5;
bool isMember = true;
string joinDate = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
string json = $@"{{
""name"": ""{name}"",
""age"": {age},
""score"": {score},
""isMember"": {(isMember ? "true" : "false")},
""joinDate"": ""{joinDate}"",
""nickname"": null,
""hobbies"": [
""drawing"",
""football""
]
}}";-
Quotes vs. No Quotes:
-
""{name}""and""{joinDate}""have""around them because strings and dates must be inside quotes in JSON. -
{age}and{score}have no quotes around them because numbers in JSON are never quoted!
-
-
Why
{(isMember ? "true" : "false")}for Booleans? In C#, if you print aboolvariable directly ({isMember}), C# capitalizes the first letter (TrueorFalse). However, JSON strictly requires lowercasetrueorfalse! Using{(isMember ? "true" : "false")}(orisMember.ToString().ToLower()) ensures it outputs lowercasetrueorfalse.
Wait—what happens if name (or a bio/message string) contains a double quote (") inside it?
For example, suppose a student's status message is:
I love "C#" programming!
If we plug that raw string directly into ""status"": ""{status}"", the resulting JSON becomes:
"status": "I love "C#" programming!"Look closely at the quote right before C#: JSON thinks that quote is the end of the string, and your entire JSON breaks with a syntax error!
Just like we used WebApp.HtmlEncode() in Day 6 to make text safe for HTML, cshttp provides WebApp.JavaScriptStringEncode(text) to make text safe for JSON!
string rawStatus = @"I love ""C#"" programming!";
string safeStatus = WebApp.JavaScriptStringEncode(rawStatus);WebApp.JavaScriptStringEncode() automatically escapes any " inside the text into \" (and escapes backslashes \ into \\ and line breaks into \n), so the JSON string is guaranteed to never break!
Let's create our HandleStudentApi() method that builds our structured, indented JSON using $@"..." and sends it to the browser with the label Response.ContentType = "application/json";:
static void HandleStudentApi()
{
string name = WebApp.JavaScriptStringEncode("Alice");
string status = WebApp.JavaScriptStringEncode(@"Learning ""cshttp"" & JSON!");
int age = 12;
double score = 95.5;
bool isMember = true;
string joinDate = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
string json = $@"{{
""name"": ""{name}"",
""status"": ""{status}"",
""age"": {age},
""score"": {score},
""isMember"": {(isMember ? "true" : "false")},
""joinDate"": ""{joinDate}"",
""nickname"": null,
""hobbies"": [
""drawing"",
""football""
]
}}";
Response.ContentType = "application/json";
Response.Write(json);
}When you visit http://localhost:8080/api/student in your browser, your server sends back:
{
"name": "Alice",
"status": "Learning \"cshttp\" & JSON!",
"age": 12,
"score": 95.5,
"isMember": true,
"joinDate": "2026-10-27 18:55:00",
"nickname": null,
"hobbies": [
"drawing",
"football"
]
}Now let's build HandleHome() so our Home page (/) fetches /api/student using JavaScript's fetch() and displays each property!
In Day 11, we used response.text() to read plain text.
Today, because /api/student returns JSON, we call response.json() instead!
fetch('/api/student')
.then(function (response) {
return response.json(); // <-- Automatically parses the JSON text into a real JavaScript object!
})
.then(function (student) {
// Now we can access every property directly by name!
document.getElementById('studentName').textContent = student.name;
document.getElementById('studentStatus').textContent = student.status;
document.getElementById('studentAge').textContent = student.age;
document.getElementById('studentScore').textContent = student.score;
document.getElementById('studentMember').textContent = student.isMember ? 'Yes (Active Member)' : 'No';
document.getElementById('studentDate').textContent = student.joinDate;
document.getElementById('studentNickname').textContent = student.nickname === null ? '(None / null)' : student.nickname;
document.getElementById('studentHobbies').textContent = student.hobbies.join(', ');
});Look how powerful response.json() is:
-
student.namegives you"Alice". -
student.agegives you the number12. -
student.isMembergives you a real boolean (true), so we can usestudent.isMember ? 'Yes' : 'No'! -
student.nicknameis a real JavaScriptnull. -
student.hobbiesis a real JavaScript array (["drawing", "football"]), so.join(', ')turns it into"drawing, football"!
Here is the complete, clean Program.cs for Day 12, keeping only /home, /api/student, and HandleNotFound():
using System;
using cshttp;
namespace ConsoleApp1
{
internal class Program
{
static void Main(string[] args)
{
WebApp.Run(8080, OnRequest);
}
static void OnRequest()
{
string path = Request.Path.ToLower();
switch (path)
{
case "/":
case "/home":
HandleHome();
break;
case "/api/student":
HandleStudentApi();
break;
default:
HandleNotFound();
break;
}
}
static void RenderPage(string title, string bodyContent)
{
string html = $@"<!DOCTYPE html>
<html>
<head>
<title>{title}</title>
</head>
<body>
<nav>
<a href='/'>Home</a> |
<a href='/api/student' target='_blank'>View Raw /api/student JSON</a>
</nav>
<hr />
{bodyContent}
<hr />
<footer>
<small>Server Time: {DateTime.Now:yyyy-MM-dd HH:mm:ss}</small>
</footer>
</body>
</html>";
Response.Write(html);
}
// NEW IN DAY 12: Sends structured, indented JSON with strings, dates, numbers, booleans, null & array!
static void HandleStudentApi()
{
string name = WebApp.JavaScriptStringEncode("Alice");
string status = WebApp.JavaScriptStringEncode(@"Learning ""cshttp"" & JSON!");
int age = 12;
double score = 95.5;
bool isMember = true;
string joinDate = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
string json = $@"{{
""name"": ""{name}"",
""status"": ""{status}"",
""age"": {age},
""score"": {score},
""isMember"": {(isMember ? "true" : "false")},
""joinDate"": ""{joinDate}"",
""nickname"": null,
""hobbies"": [
""drawing"",
""football""
]
}}";
Response.ContentType = "application/json";
Response.Write(json);
}
// NEW IN DAY 12: Fetches /api/student and reads each JSON property using response.json()
static void HandleHome()
{
string content = @"
<h1>Student Profile (Loaded via JSON)</h1>
<p>Click the button below to fetch structured JSON data from <code>/api/student</code>:</p>
<button onclick='loadStudent()'>Load / Refresh Student JSON</button>
<br /><br />
<div style='background: #f9f9f9; border: 1px solid #ddd; padding: 15px; max-width: 450px;'>
<p><b>Name (String):</b> <span id='studentName'>-</span></p>
<p><b>Status (Escaped String):</b> <span id='studentStatus'>-</span></p>
<p><b>Age (Integer Number):</b> <span id='studentAge'>-</span></p>
<p><b>Score (Decimal Number):</b> <span id='studentScore'>-</span></p>
<p><b>Is Member (Boolean):</b> <span id='studentMember'>-</span></p>
<p><b>Join Date (Date String):</b> <span id='studentDate'>-</span></p>
<p><b>Nickname (Null):</b> <span id='studentNickname'>-</span></p>
<p><b>Hobbies (Array):</b> <span id='studentHobbies'>-</span></p>
</div>
<script>
function loadStudent() {
fetch('/api/student')
.then(function (response) {
return response.json(); // Parse the JSON text into a JavaScript object!
})
.then(function (student) {
document.getElementById('studentName').textContent = student.name;
document.getElementById('studentStatus').textContent = student.status;
document.getElementById('studentAge').textContent = student.age;
document.getElementById('studentScore').textContent = student.score;
document.getElementById('studentMember').textContent = student.isMember ? 'Yes (Active Member)' : 'No';
document.getElementById('studentDate').textContent = student.joinDate;
document.getElementById('studentNickname').textContent = student.nickname === null ? '(None / null)' : student.nickname;
document.getElementById('studentHobbies').textContent = student.hobbies.join(', ');
});
}
// Load automatically when the page opens
loadStudent();
</script>";
RenderPage("Home", content);
}
static void HandleNotFound()
{
Response.StatusCode = 404;
string content = @"
<h1>404 Not Found</h1>
<p>The page that you are looking for does not exist.</p>";
RenderPage("404 Not Found", content);
}
}
}- Run the application (
dotnet runor press Start in Visual Studio) and visithttp://localhost:8080/. - First, click View Raw /api/student JSON in the navigation bar (or visit
http://localhost:8080/api/student).- Because we formatted our
$@"..."string with clean indentation and setResponse.ContentType = "application/json", you can clearly see every JSON data type ("Alice",12,95.5,true,null, and["drawing", "football"])! - Notice how
"status"shows"Learning \"cshttp\" & JSON!"—WebApp.JavaScriptStringEncode()escaped the inner quotes automatically!
- Because we formatted our
- Now go back to
http://localhost:8080/.- All 8 fields of the Student Profile Card are populated automatically by
loadStudent()! - Click Load / Refresh Student JSON a few times—watch the seconds in Join Date update live without reloading the page!
- All 8 fields of the Student Profile Card are populated automatically by
| Feature | Day 11 (Plain Text) | Day 12 (Structured JSON) |
|---|---|---|
C# Response.ContentType |
"text/plain" |
"application/json" |
| What C# sends | One flat string ("Buy milk...") |
Structured { "key": value } text |
| How JavaScript reads it | response.text() |
response.json() |
| What JavaScript receives | A single string
|
A real JavaScript object (student.name, student.score, student.isMember) |
In Day 13, we will learn how a popular C# library called Newtonsoft.Json can automatically convert any C# class or List<T> into formatted JSON in one single line of code—so you don't even have to write the "" quotes or { } braces by hand!
Technical Specs
14 Days Tutorial