Json Parser from Scratch in C# using TDD

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
welcome to the Rock Running YouTube channel my name is Anton and today we're going to be building a Jason parser primarily for educational purposes this is not going to be a serialization and deserialization from one type into Json and back this is more of a Json document scenario where we have a blob of Json we parse it into a structure which we can then manipulate in c-sharp if there are any features that you would like me to implement on top of this Json parser leave a comment and find other comments with other features leave a like on those features and if one gets more than 100 likes or something like that I will Implement that feature as a side note I am going to leave out some features from this Json parser to cut the video short for example like floating Point parsing and things like handling cyrillics and Chinese characters Etc please don't forget if you're enjoying the video leave a like And subscribe if you have any questions leave them in the comment section again about the features on top of this Json parser that I'm gonna build if you would like to see one of them go into the comment section and like like the feature that you want to see don't forget to check out the description I've already built something like this during my live streams on Twitch so go ahead follow that I have a c-sharp course that is out if you would like to know C sharp as I do it I highly recommend you check it out with that let's go ahead and get started here I have a Json parser solution with a Json parser this is going to contain the Json class which is going to be doing the parsing we're then going to have unit tests on the side to make sure that I'm not regressing and that we have some kind of plan that we're going against before we dive into writing any code let's understand what we're trying to do from a high ground we are going to have some kind of blob of Json The Blob of Json is essentially a big string another thing that a string is is just a collection of characters okay so these three things are essentially the same so I'm putting it three-way equals here all right for the parser that we're going to have what it wants to do is it wants to go from from the first character and then Traverse every single character in this array as we're traversing the characters we're going to keep some kind of track of what it is that we're going to try to parse so for example if we encounter an object we're going to expect to have a key after the key we will have a colon and then we're going to expect some kind of value if the value starts with a double quote it is going to be some kind of string if the value starts with an N we are going to expect to see no all right so as we see null from this first character and we can say first thing is okay just Advanced Four characters forward so instead of going character by character we're just gonna skip over to the new line or whatever it is going to be on the end right so this is backslash and a very bastardized one but you get it another thing that is happening here is we take this null and we save it into a token so as as we're traversing this collection over here what we're doing is actually mapping it or aggregating it into a tree okay so in the end it is going to look something like this all right and what we are going to be returning is this root node or some kind of object that is going to be pointing to the root node and then it is just going to be a very good interface to basically use to say Okay I want to go to this property I want to get this property I want to get the value in that property with trying to convert it to a Boolean or something like that okay so that's the high plan we started talking about null so let's see what this is going to look like so the first test is going to be personal we are going to take our Json parser we're going to parse a value of null this is valid Json by the way we don't have a method so let's go ahead and create a method in here the no will just be some kind of Json value if we want to do this Uncle Bob's DDD style we can have some kind of result so far a result this doesn't return anything just yet so let's go ahead and return an object and then we want to have an assertion that it is no so result should be no coming back to the bars Jason is going to contain null and if we are really silly we can just go ahead and return null over here but let's not do this we are gonna have a for Loop and for Json lengths we're going to extract a character from Json I if character let's say is n the value so result we'll say from the beginning it is null we should reset the result to null because that's what it is and then we want to increment I by four so from the current position and this I over here we're just gonna get rid of it all then continue because we are about to have other characters that we're looking at otherwise let's go ahead and return the result I do have nullables over here so I'm going to put up question mark over there let's run the unit test and the unit test is passing let's then copy this across we'll say parse bull for this we can use a theory going to be the expected result same thing over here so false and false first parameter is Json second parameter is expected let's take the Json parse it as the expected is actually a bull the result we can assert off type or is type bull result let's say that this is value and then we're going to assert equal expected and value close that window and run the tests here we will get a couple of failing tests or should at least and actually that's an infinite Loop because we are not Advanced advancing the increment here in any way so let's Advance it and hopefully by looking at this code you can see that it's a little bit hard to read and there are things that we can do here but I will save it for later in case people actually do want to see it so if we are encountering T we want to return true and increment by 4 if we're encountering F this is going to be false and one more character so incrementing by five the increment over here is actually a little bit suspicious we can throw a new exception that says something like unknown character okay if what we're seeing is something like a space there is no result of parse out of here and we can actually just increment the counter if I open up the tests let's go ahead and rerun them everything is passing and now let's move on to the numbers so instead of pars bull we will have pars number this is going to be an integer we are going to be parsing an integer let's say We'll begin with a zero one something like ten let's incur or incorporate some I don't know funny numbers I am going to be excluding things like floating points and we can put a couple of spaces over here to just say that it doesn't really matter same as before let's open the test Explorer run all tests we have the number tests failing which is fine so if we go over here and we put an if statement and the character starts with a one or any other number and we will have to repeat this a couple of times for all the different numbers like 1 and 2 at cetera and actually the easiest way to do this here essay is zero or is one and so on and so forth now that we have something like this what can happen is the number is not just a singular number it is going to be multiple numbers in a row we don't know how many numbers it is going to be in a row and this object we would have to cast it on cast it Etc so one of the things that you can do here is say you will return some kind of integer so parse number you'll have string Json you will also have the position let's scroll down a little bit here I'm going to grab this if statement as well and well we're here let's say the result is parse Json we pass it along the position is I we're not incrementing it and this will have to be static if we do want to increment it we actually have to say how many things we have parsed over here so let's return a tuple of integer integer this is going to be the result and this is going to be count so how many counts have we advanced again going super simple you're going to have some kind of result tons of things that you can do to optimize this however I will leave it as I don't know as palpable as possible we're gonna go for the length of Json still however this time we're starting from the position that we have passed over here all right and this parse is actually parse number again take the if statement place it over here I'll scroll up and again grab this character place this over here as long as the character is what we expect it to be we want to go ahead and append this character onto the result as soon as we encounter a character that is not a number we want to break for our terrible solution and the thing that we're going to return is result a length is how many things have we parsed so we can advance the cursor that many times and then the actual result can be and parse the result so coming back over here we can take the result save R increment or account and then the actual result or number take the result assign the number to it take the I increment it by increment semicolon on the end come back to the tests let's go ahead and run them and so far everything is passing great now let's take a look at a string so let's take the theory over here I'll delete a couple of them because what I want to have is an escape string let's say something like one and then the result should actually be just one this is the expected result string let's also verify the same thing here this is going to parse string let's also say we want something like with a space two and a space after here so we know that we are not skipping these spaces and then one of the most interesting things I think will be actually having things like escaped quotes right so for our third case three we're gonna have another double escape with another Escape of this quote and the final result that we're going to be looking just with single escapes okay space here on the end I will actually remove it let's go to Json we can take the save statement here if C is double quote the rest can go and we're gonna take a similar approach instead of parse number we are going to have parse of word or parse string let's have our string still the same result if C is however double quote that is when we want to break all right so let's take this cut this out we're going to break then for the actual result length that is going to be what's contained within the double quotes we are going to slap on plus two on that for the quotes that are around the actual string that we're parsing and then the result we don't want to parse this is just going to be a string that we return in the simplest scenario we're just gonna take whatever character is coming our way and append it to the string in the edge case scenario where we have that backtick or sorry not backtick the backslash so I will have to escape it over here we actually don't want uh the backslash itself we want the character that comes after it then let's just remove the Steph statement because the thing is shouting at me right we are just gonna skip past this character so we will increment once we're then gonna take whatever is in the next place assign that and then as we're passing along we're gonna go past that character and grab the next one so this is what we have here our string let's grab that come back up here Place bar string and the same thing except for the number we will have a string I'm going to use Str if you can type string because it's a keyword you can always do something like this okay but Str is my preferred approach with this I think this actually should be fine let's go ahead and rerun all the tests I should really learn the keybinds for re-running all of the tests we have a couple of tests failing looks like none of this has actually worked and I think that's because yeah so character whenever we're encountering the double quotes we go in here and we start off with the same position of the double quote all right so we actually just want to advance once further as we're beginning our looping all right uh let's rerun these tests uh the escaped character test is failing so what we've expected to be here isn't actually working and that is because nobody will be able to tell you why let's put a breakpoint over here we're going to run this test we have the character space so that is fine we should be appending that and there we have a c set to the not the back text sorry the backslash so we're advancing one and then the next thing that we're setting to the result is the actual quote so then the next thing if we skip past this should be the T I am quite confident this should be working as is let's actually play this to the breakpoint over here so I'll move the one here there is three for the result play to the exception and we exit so the thing that I've managed to spot there is essentially as we are going past this increment over here we're not actually appending it to the result the result of length that we have over here doesn't actually match how many characters we have traversed so what happens after it is we're probably sorry somewhere over here we're just overriding the original string that we parsed for this uh swapping to something like a while it would probably be the easiest solution we have the position we have the I while I is less than length we want to go ahead and increment I we're going to be incrementing it every single time We're looping around and then at the end we can do something like I minus position and probably par plus one for the time that we're not gonna increment it here if we want we can remove the plus one over here and actually just increment the eye over here all right as a Scottish person would say I rerun the tests and everything looks good now so we have our four data types and what we're really doing here is we're parsing a value so the parse function over here parses a value as soon as we get around the two arrays so we'll say parse array with same types or same values and not arari array and same value types will be maybe a little bit hard to represent with bull types okay this can be a fact no parameters and the Json that we're going to be supplying is an array with things like true and false the result should be an object array we're then going to grab our assertion of type or sorry is type and bull we're not going to assert the length hopefully it will crash without it so the first one is Boolean so B1 for Boolean one we're gonna assert that that is true so B1 and then B2 is not going to be true so LS actually grab this put this here here remove that one over here if we jump into parse we kind of want to zoom out now this is not really parse this is parse value if I duplicate this place this over here our parse function should really try to understand is it attempting to parse an object is it attempting to parse an array or is it attempting to parse a value the result of parse value becomes something similar to how we use to parse our number in string because we want to parse a value and then we want to return whatever the counter was again the integer is going to be the count object is going to be the value we will replace the for loop with a while loop as soon as I remember how to type take the I place it outside remove the semicolon format everything and there we go I'll actually take this while loop or the beginning of it place this over here and say something along the lines of if the current character that we're looking at so see if it begins with an open Square brace that is where we want to again create another region where we parse a array so this is an object arrays and that's going to be the value or both parse value and parse array we will want the same integer position to pass into the functions so let's go ahead and add that inside the if statement over here again we will employ the same tactic where we're parsing a number and then we're gonna get an increment a and a number or in our case it is an array this is going to be parse array internally it is going to take care of things like skipping over spaces commas Etc okay same way that this is actually going to take care of skipping over spaces okay so if we have something along the lines of this where we have a space over here we can actually just skip over it and nothing is going to happen so this can actually happen somewhere in the beginning maybe otherwise as a fallback we can parse value this is going to be some kind of value an interesting point here is that maybe we don't actually need the increment over here as soon as we encounter one of these characters we know the rest of it is going to be the full thing it's only once we encounter this recursive structure where we have an array or we have an object and there's object or arrays within object or within a race on and so forth we actually just kind of want to say look for whatever thing we get over here just return the value and we're gonna do the same thing over here return value otherwise we're gonna throw new exception unknown or failed to parse Json maybe a new line and something like Jason so we'll just put out the thing that we failed to parse okay but with this we still have the parse array function to implement so let's take this while loop over here place it inside the eye is going to start from position plus one if we are encountering spaces within an array we're just going to skip over them as soon as it's some kind of value we actually want to parse it in this case we're not going to be using parse value although we can we are actually just gonna go straight back to parse and this is where this while loop actually makes sense here and you actually want to return it so what I will do is again I'll just copy this because again I'm saying that I'm interested in this I number over here otherwise not really so let's introduce a for Loop place this over here remove the eye and make this Loop not a comma here not a semicolon here failed to parse Json we don't really need it one too many spaces here and this is going to be an internal parse this function needs or it should be private this should return an integer so how many counts has it traversed to try to parse whatever it is trying to parse so let's say count here and then the actual value over here we'll take internal parse and maybe it was a little bit of a moot point of changing this to a for Loop kind of just want to pass the Json over here grab the value one of the things that should go in here as a parameter is because we can be passing parsing any of these arrays or values or objects as a nested structure so here we're going to be starting from zero and the point here is that we're actually discount discarding this number all right for the rest of the stuff we actually want to take the increment and the value or in our case here it is going to be the array the I starts from the position and I think it's safe to actually just say we want to return by however much we have incremented or any white space that we have managed to leave out and semi colon over here the value actually get rid of that there I'll place the else statement over here put this inside here this here that that there you know not very good descriptive words uh let's take this parse Value Place it above and we're gonna again have this increment and then the actual value do the same return take the vowel place it over there and format the code I think that is about it so while we're traversing inside the array we're skipping all the spaces if it is not a space we actually want to use the internal parse so internal parse we pass along the Json we pass along the eye this is going to give us some kind of increment some kind of value that we have managed to parse there is going to be a result so an object array and would actually be very useful to know how big the object array is going to be but I'm not going to know that so the result is going to be a list to be begin with we'll take the result we're going to add value it does want these objects to be nullable just in case and then by the end a result to array and of course this is a tuple where in the beginning we did have the position with whatever ISO I minus position then we have the comma the object array here can be nullable as the I incrementation should happen right over here and then we loop around if there is a comma we just want to loop around because these two look very similar we can essentially treat space and commas identical so something like this if the character is an actual closing brace that is when we want to exit now let's take a look there is one more error just jumping down here I want to see what's happening and that is because we actually changed the parse value with these counters so we have the position over here let's again set it initially to the I again same thing for the return result we're gonna have I minus position that is how much we have traversed we then have the result and I think that actually looks okay let's go ahead and rerun the tests uh let's see what failed we have an exception the exception has been thrown at unknown character and maybe somewhere over here so we have true false first we went into parse into internal bars we try to parse the array which is correct we then try to use internal parse again and internal parse went into parse value which to me sounds great correct again and then the character was unknown at that point okay so perhaps it is when we are going into the internal parse again something is happening with regards to this Square brace we're encountering the square where brace we're passing down the position as if we're still at the square brace all right maybe what we want to do is we actually want to increment yeah we have consumed this character and perhaps it is going to be a smart idea that whenever we're doing this incrementation just do it straight after we have consumed that character let's scroll down to the array we're gonna try to do the same thing over here of what we just learned maybe even combine it like so come back over here do the same thing here I know cleaning up this code because it's a very procedural it's a steaming hot pile of anyway right for the numbers we don't want to do it for the characters we actually might want to do it if we come back down here or the par string maybe we no longer need to add this additional one over there okay uh let's come back over here rerun the tests still one test failing or actually all tests failing now so did end up regressing all the way to the Oblivion let's actually undo all of these changes and we're just gonna do it for the initial brace right so where I was going to place it over here time travel all the way back when we encounter this brace that's the only time we want to increment so when we go into parse array that is specifically where we start to look for okay if it's not this brace we're gonna try end up parsing a value hopefully okay rerun the tests uh still failing add a known character when we're trying to parse value so for the unknown character we can actually place a breakpoint over here debug the test and quickly take a look at it and we're actually looking at character R so however we're traversing what's happening is we're incrementing things a little bit too much which I thought wasn't actually a problem so let's take a look at this if we're looking at an array we're reading in a character it is not going to be empty so we don't increment anything let's say we increment once here because we are seeing a Square brace what I'm going to do is I'm going to remove this from here I'm going to go into parse array and because in parse array we have the plus one here okay so I will leave it at that because we have the same thing for the string I'm going to rerun this and this is still going to be an unknown character I'm going to debug it something that I should have probably done in the beginning when I encountered this error and we're again going to take a look at this state so this time what we're looking at is a comma something that we should have skipped past I have a feeling it is because the let's say internal parse when we are parsing the value the value should come back to the array we take the value we added we increment we increment and then this should definitely go over so let's place a breakpoint over here after internal parse we're going to debug this again and we're not reaching the breakpoint which basically means we never exit this function which is actually understand in all of these statements we have continue where really we want to break once we have gotten our token let's say we just want to exit right so in all of these places let's go ahead and break instead of continuing I'm going to come back I'm just going to rerun this test and I think at this point it should pass although true false uh this should be parsing as false so this is just a wrong test let's rerun everything and looks like everything working okay let's do the same for other types so let's say number types we're gonna say 1 69 and 420 one space in between here and here and here just to make sure that they don't actually matter let's say equal and equal 69 one over here as well 420 over here I'll say that this is two all of these values should be integers let's go ahead and run this test 100 wasn't happy with that so it actually managed to parse the first and the second but not the last value which seems a little bit odd to me so let me put a little breakpoint over here just double check the the things that we're passing or that the formatting of the the output isn't actually messed up so let me step one remove this breakpoint play taking a look at the value so 169 and the f420 We are failing to parse 420. how do you debug something like this so let's come back we're gonna go to parse array I'm gonna place a breakpoint right over here I'm gonna debug this test just as it were like a minute ago so we're currently looking at character one understandable everything works over here so if I just go past this we're gonna be or we were looking at characters six the current I is at 6 and incrementation actually went up to eight so when I increment this the I is actually gonna go overflow and we're gonna miss 420. okay so something is wrong with calculating the actual number in the parse integer so let's see the the this all of these eyes will start from the position when we're returning over here here we are subtracting I from the position and this looks all fairly standard for the parse number just double checking that nothing is messed up over here we set the result we set how much we want to increment by we're doing this by the result here and to be fair thinking about this now we're doing this silly breaking over here for how much things we have actually managed to parse we could probably just go ahead and do something like return however much you need to increment okay so this can actually be a return statement same return statement over here get rid of this and the space can skip no problem and I would actually rather put this somewhere at the beginning over here or the N or null let's return four and null get rid of all of this stuff for the result here again for Andrew get rid of this stuff for the false return 5 and false looks good to me and for the rest I mean this is going to throw but yeah as soon as we encounter a token that we actually recognize in any of these positions that we should actually be all right uh the bit over here I'm not actually sure how we're going to reach this so I'm just going to throw another exception over there okay uh with that hopefully the numbers should look a little bit better let's open up all the tests and run them again uh the one test is still still failing so let's debug it so on the first one everything is fine and on the second one again we're incrementing a little bit too much so on the internal parse the amount that we're incrementing should actually not have to do anything with that and not anything with this either so complete Mistake by me let's just return however much we should increment from here I think that should be fine then let's stop this test and we run all of these and it looks like everything is passing cool let's come back to the test we are gonna do some strings now so again one two three where the three is going to be looking a little bit funky with string types amend all of the assertions rerun the tests and it looks like we do have an unknown character somewhere over here where we're trying to parse a value from looking at this I can confidently say that whenever we're parsing a value we don't actually expect it to start with backticks and actually I don't think this is valid Jason so if this is a string this is a string this string should actually be enclosed within quotes right otherwise a string value cannot start with a backslash all right let's place this over here and no spaces in between the outer quotes and and bring up the tests rerun cool and it look all looks fine let's do a dynamic array so Dynamic probably leave the types remove everything from there we'll start with null one false and then the one over there copy this one time picks up all of the assertions first is North the second one is one then we make sure it's false and the last one is equal to one run this and it looks good so let me just run the whole thing uh one more test to do is parse array with nest nexted and nested we can do first as Dynamic and then one two three and maybe even false and one the kind of same tests but in a nested array right over there okay the value I am going to override it right over here so the the second is object array again semicolon on the end change the indexes to be zero one and two run this test well looks like everything is working out so I am confident it is working we have the array functionality I think finished at this point uh we basically then want to move on to the objects right so the object is the thing that is kind of well parse array we kind of just let's copy this place this over here we want to parse an object for most of the stuff in the middle let's get rid of it and we're kind of gonna start a new uh the things that we have over here and the value that we're going to return is actually going to be a dictionary from string so some kind of key and this may return an object that is going to be no not sure why it was giving me a big string over there this barely fits on my screen all right but I'm zoomed in like into Oblivion so hopefully shouldn't affect you the result is going to be this dictionary I will just return the result over here and we're gonna do things such as inside the internal parse if we're starting with a curly brace we are going to parse object right nice super simple starting from position one we're gonna skip the inter initial curly brace if we are facing like something like a space let's increment and continue right so a space we don't really care about if what we're facing is going to be a quote then things start to get a little bit interesting we actually just want to go ahead and parse a string we're gonna pass the Json we're going to pass the position or rather the I one thing to remember is that internally it's going to position itself on the right side of this bit this bit or this quote we're gonna get back an ink with a key so this is going to be the property key this function did end up being public I'm just going to mark it as private and here hopefully you can see how now we want to look for a colon and we want to look for another value until we reach a comma or we reach something like a closing brace okay so we're looking for all of these characters Maybe what's going to be a little bit easier is to have an additional method that is going to say parse object property so at this position we're going to have a property we shouldn't have any spaces we're going to start off with parse string okay here we're gonna say we are expecting to give out a key and we're going to expect to give out a value so VAR Inc a little bit of space here key and value from parse object property uh pass the Json the position I we want to increment by a much the result key should have the value and then maybe we need to continue maybe not other things that we may encounter if we have a comma after that we don't really care about it or let's say we have something like a new line but we don't really care about that either what we really care about is if we have a closing brace if we have a closing brace then that means I should be breaking out because that's where the object completes or the parse string over here when we're getting around to the property please remember that we are already looking at the Double quote okay and this is going to go past the double quote once we have this we're going to take the I we're gonna append the incrementation and we're gonna hold on to the key if we ever exit this Loop this should really be an exception so let me I just don't want to rewrite it so I'm just gonna throw this here and at the end what I'm gonna return is I minus position key and then the value which is currently missing I will then have another while loop over here where I'm going to check while whatever character it is that I'm looking at it doesn't equal colon all I'm trying to do is get over on the other side I actually just want to increment it so I'm just gonna keep incrementing it as soon as it is colon after it I can increment it again I know this looks a little bit silly but we're really just using our raw thoughts to make up this solution incrementation we're gonna have a collision over here because this is going to be the value and because we can have nested objects this is going to be parsing an object so this is going to be value Inc and this is going to be key Inc key Inc we're going to increment it here value Inc we are going to increment it here let's remove the spaces here and I think uh that should actually be fine the result doesn't matter the character that we have been looking at here it doesn't really matter perhaps uh the while loop over here also doesn't matter and the throat doesn't matter here either if we fail to do something in this scenario hopefully if things are going to be evident so again we're encountering this quote we're going into the parse object and the plus one over here that shouldn't really be here because the power string is going to be going in there and we actually have the I over here and yeah that is fine that is basically how we keep track of how far along we have traveled maybe what we want to do and this is again up to you is if you have a position you can actually just return up to where you went and all your doing is just you are I don't know wherever we're doing the incrementations you don't do an incrementation you just reassign the number so anyway we did all of that let's just make sure whatever we have still works perfect I don't look like a complete imbecile uh let's take this place this over here now we're gonna say parse object let's remove this we're gonna place a parse this is going to be a dictionary of string uh object no array over here this is going to be a value and we will assert equal maybe was there zero for these uh zero or empty value right so this is just going to be empty bars and we'll actually call it like that as well so empty object let's run this and there we go okay perfect I'm actually surprised that works so parse will say flat object where the Json I will have to get a little bit more creative with this ring now we might do some mobile like this like that uh remove the quotes give it a little bit of space I'll actually zoom in so people can see I'll take the Json place this over here and this is now not empty take the assert this is going to be null value at position a or rather at Key a this is then going to be true at Key B this is then going to be equal to 360 at PC right over here and we can also insert is type and say that this is a Boolean the final thing is going to be no scope in position D all right flat object let's see how this handles maybe some of the assertions are going to fail it definitely looks like an infinite Loop so maybe it was the comma or the trailing comma though I thought I said to ignore it uh I'm working with very little real estate here uh let's try this no Trail and comma for a single property if that still doesn't work then we will actually have to go and take a look at some of the loops that we have so into parse object we want to go into the internal parts we are going to encounter our opening curly brace we're gonna go past it so we're somewhere over here we then may have a space we may have a new line and every single time we have something like this we are just crawling I know crawling past it uh maybe uh we are so then at some point we're going to encounter this thing right here we will then want to parse object property at this position we're going to parse a string we're going to advance we're going to advance some more and some more we're then gonna finally parse an object we're gonna move across the correct amount and then we're gonna return stuff I'm saying this loop I don't think the infinite Loop can actually be here because at some point we're gonna run out of the index of the array this Loop is in a similar scenario unless we're actually encountering some kind of character that isn't matching any of these and we're then somewhere in the astral or The Ether of this right I'm actually curious what could that character be let's head on over here uh let's debug and this is when we're parsing object okay so we're currently at the First new line we then have a bunch of empty spaces or empty characters and I'm hoping okay no so uh for a second there I was thinking that the spaces over here were looking at just like m to characters or no character at all so I was thinking that's how it got stuck there but never mind so here I am hoping it goes over over to parse object property we're then gonna jump over and interesting that we're looking at n as a position so the first property has failed us okay so the only ends that we have over here is this and and this end we have removed all the properties so the this end is really the culprit so what I'm thinking is somewhere after here we're not incrementing things correctly so potentially we might want to take a look at this area where we're parsing the object property I'm expecting this parse object okay so parse object isn't actually okay because it can be an object or it can be an array or it can be something else so what should be here is actually internal parse and I actual internal parts and yeah now it's actually plugging this up as nullable so yeah object nullable I think that should actually be okay is my debugger still running and no it's not let's rerun the test I think that should actually fix it spoken too soon looks like we still have the infinite Loop let's debug this again and actually let's go into this Json parser again what I want to do okay I have a break breaking point right over there with my debugging menu sorry if this is jumping about a little bit too much we are currently uh let's get past this skip past this fast fast so the first double quote we have now managed to parse something the key that we're parsing is backslash n and the value is null okay so understandable first of all we're passing the wrong position here so just the initial one it's definitely not something that we want to be doing we want to be passing I so right off the bat having two parameters for where we've been and where we're going is already kind of like a list of this and before I rerun the test again I just want to double check that I'm not doing the same mistake over here it doesn't looks doesn't looks doesn't look like I am all right let's rerun another infinite Loop definitely do something along the lines of this where we'll come back down here I'll say continue and then throw exception and unknown token and then we can actually output the actual token we're looking at right smart make sure that we have a new exception quickly see if we can get a good enough output unknown token l so where is it grabbing L from somewhere from null somewhere over here okay interesting I think I know what this again this is all going to come down to this whole eye Malarkey when we are parsing all of these objects again uh it kind of just annihilates this eye whenever we're walking over any spaces or anything like that just kind of erases all of that information so if we have our object over here and we have managed to pass some kind of distance we want to return I'm minus position so whatever other distance we have managed to pass plus increment and then the final object okay oh what are you gonna do uh we're gonna have an array over here uh return an array and then the same thing here the ink and then value equals same thing here except we'll have V Inc and in case you're wondering why the ink that's because otherwise we get variable Collision all right so that's the first place where I was making that mistake uh the other place was definitely wasn't there not here either parse array appending everything here this bit I feel like yeah so same mistake so I position let's go ahead grab it place this over here over here let's say bar Inc number equals same thing return Plus Inc number uh same for the string so ink strip return or sorry that was ink and string talking like a robot there be Boop up let's give this a go so I'm just gonna run all tests here and actually all of them just passed there so okay so as long as we manage to correctly track how many characters we have traveled and I'll be honest the whole static function this is uh like procedural programming 101 no objects no Global State just a local state this is horrible I'm fully aware of this solution I'm gonna try to make iterations on this to highlight actual programming paradigms so we're going to be rewriting this solution bars nested object so let's say we will have these properties and then and B There we go so now for the assertions so after extracting the original dictionary we just re-extract another dictionary with similar values and just run that assertion so with nested objects let's give that a go looks like that is passing and now we want to say with the nested object and arrays here I will take this property again I'm just gonna assign this to be something along the lines of this tried my best at indenting it so this should still be null uh the B that we're grabbing everything should be identical except the last B over here what I'm gonna be grabbing here is going to be an array so this is an object array to remove the true the very first value in the array should be an object so just this dictionary and again the properties should all be identical so I think this is more or less the last test that should be run over here and of course we get a little bit of an exception specifically the exception is right over here for the value that we're trying to parse an unknown character let's at least see what kind of character we're trying to parse and uh I'll be honest uh the best error that seems at this point is actually spinning out a substring from the beginning four length of I let's say plus one and then we can see actually how long we have managed to get or how far so inside the array that is where we failed so what we do is we go to parse array let's go into there and one of the things is we haven't accounted for the fact that a race could be multi-line okay uh let's slap the new line in there that we can actually Traverse it and now all of the tests are passing cool so let's review on all of the tests again everything is passing and I think that is pretty much it so a couple of things that we're missing over here is when I talked about having this token tree we don't really have a token tree we are just kind of saying okay return this object and the object is the token but inside of there there may be a string or there may be a null so we don't really have our structure for the tokens another thing that we don't have is again because we're lacking the tokens is this interface we're kind of just saying get a Thing cast it and so on and so forth we are assembling a nested structure we're just missing the actual data types from it so the approach that we took here is not specifically on purpose is a procedural approach which is just static functions now you can take a functional approach where there is a lot less State and a lot less mutations and I think one of the episodes that I'm going to make in the future is going to be around turning this into a functional solution nevertheless that will be it for this video a little bit a little bit on the longer side hopefully you've enjoyed the video how to build a custom Json parser from scratch if you did don't forget to leave a like subscribe if you have any questions make sure to leave them in the comment section if you would like me to add any specific features that you would like to see how they fit into this solution go ahead drop a comment and leave a like on that comment if you see a feature that you want to see get implemented again I will be rewriting this in a more functional way maybe even an object-oriented way although I don't think it is a good fit don't forget to join the Discord server don't forget to follow me on Twitch that is where I practice this sort of stuff if you would like the source code for this video as well as my other videos come join me on patreon I will really appreciate it and a very big and special thank you to all of my current patreon supporters you helped me make these videos as always thank you for watching have a good day
Info
Channel: Raw Coding
Views: 4,719
Rating: undefined out of 5
Keywords: c#, json, parser, custom, from scratch, tdd, implementation, example, tutorial
Id: uPEbhT0FYNg
Channel Id: undefined
Length: 52min 58sec (3178 seconds)
Published: Sun Jun 25 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.