About Monkey 2 › Forums › Monkey 2 Code Library › Compare json values.
This topic contains 5 replies, has 3 voices, and was last updated by 
 Mark Sibly
 1 year, 2 months ago.
- 
		AuthorPosts
 - 
		
			
				
February 16, 2018 at 3:11 am #13659
Here’s some code to compare json values.
I initially tried to add something like this to the JsonValue class using the spaceship operator ‘<=>’, but this lead to some highly recursive problems because the spaceship operator redfines plain object compare etc, so stuff like ‘If Self=NullValue’ would cause the stack to explode!
This highlights the difficulty involved in overloading comparison operators for reference types (ie: classes) and it’s something I’d recommend avoiding altogether. It’s still quite all right to provide comparison operators for value types though (ie: structs).
It also underlines the need for more flexible comparison functionality in things like Map, to allow you to create eg: a Map<JsonValue,Blah> using code like this without having to define <=> for JsonValue.
Monkey123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113#Import "<std>"Using std..Function CompareJson:Int( x:JsonObject,y:JsonValue )If y=JsonValue.NullValue Return 1Local r:=Cast<JsonObject>( y )If Not r Return 1Local xit:=x.All(),rit:=r.All()While Not xit.AtEnd And Not rit.AtEndLocal cmp:=xit.Current.Key<=>rit.Current.KeyIf cmp Return cmpcmp=CompareJson( xit.Current.Value,rit.Current.Value )If cmp Return cmpxit.Bump()rit.Bump()WendReturn x.Count()<=>r.Count()EndFunction CompareJson:Int( x:JsonArray,y:JsonValue )If y=JsonValue.NullValue Or y.IsBool Or y.IsNumber Or y.IsString Return 1Local r:=Cast<JsonArray>( y )If Not r Return -1For Local i:=0 Until Min( x.Length,r.Length )Local cmp:=CompareJson( x[i],r[i] )If cmp Return cmpNextReturn x.Length<=>r.LengthEndFunction CompareJson:Int( x:JsonString,y:JsonValue )If y=JsonValue.NullValue Or y.IsBool Or y.IsNumber Return 1Local r:=Cast<JsonString>( y )Return r ? x.Data<=>r.Data Else -1EndFunction CompareJson:Int( x:JsonNumber,y:JsonValue )If y=JsonValue.NullValue Or y.IsBool Return 1Local r:=Cast<JsonNumber>( y )Return r ? x.Data<=>r.Data Else -1EndFunction CompareJson:Int( x:JsonBool,y:JsonValue )If y=JsonValue.NullValue Return 1Local r:=Cast<JsonBool>( y )Return r ? x.Data<=>r.Data Else -1EndFunction CompareJson:Int( x:JsonValue,y:JsonValue )If x=JsonValue.NullValue Return y=JsonValue.NullValue ? 0 Else -1Local jbool:=Cast<JsonBool>( x )If jbool Return CompareJson( jbool,y )Local jnumber:=Cast<JsonNumber>( x )If jnumber Return CompareJson( jnumber,y )Local jstring:=Cast<JsonString>( x )If jstring Return CompareJson( jstring,y )local jarray:=Cast<JsonArray>( x )If jarray Return CompareJson( jarray,y )Local jobj:=Cast<JsonObject>( x )If jobj Return CompareJson( jobj,y )RuntimeError( "TODO" )Return 0EndFunction RndJson:JsonValue( range:Int=6 )Select Int( Rnd( range ) )Case 0Return JsonValue.NullValueCase 1Return Rnd()>.5 ? JsonBool.TrueValue Else JsonBool.FalseValueCase 2Return New JsonNumber( Rnd(10000) )Case 3Return New JsonString( Rnd(10000) )Case 4Local data:=New Stack<JsonValue>( Rnd( 10,20 ) )For Local i:=0 Until data.Lengthdata[i]=RndJson( 4 )NextReturn New JsonArray( data )Case 5Local data:=New StringMap<JsonValue>For Local i:=0 Until Rnd( 10,20 )data[ Rnd(100) ]=RndJson( 4 )NextReturn New JsonObject( data )EndReturn NullEndFunction Main()Print "Start..."For Local i:=1 To 100000Local x:=RndJson()Local y:=RndJson()Local cmp1:=CompareJson( x,y )Local cmp2:=CompareJson( y,x )If cmp1<0 Assert( cmp2>=0 )If cmp1=0 Assert( cmp2 =0 )If cmp1>0 Assert( cmp2<=0 )EndPrint "Success!"EndFebruary 16, 2018 at 4:01 am #13660So, let me see if I understand correctly… if the json values are identical it returns zero?
(I always forget what the “<=>” operator does…)February 16, 2018 at 4:35 am #13661Yes, x<=>y produces…
an int value=0 if x=y,
an int value<0 if x<y or
an int value>0 if x>y.
February 16, 2018 at 8:42 am #13664There may be a virtual method CompareTo:Int( value:JsonValue ) of JsonValue.
By default it will return -1.
And any sub-classes will override it.
Technically it will be the same as your functions above.
The difference is a way of call: json1.CompareTo( json2 ) in this case.
If we aware of nullability of json1, then we can use ?. operator.
Also in your code you check y=NullValue and return 1 if true, but x can be null itself and then we should get zero in result.
Sorry, I did’n understand about stack exploding reason.
If we use just functions like ones above – do we avoid stack exploding when using if Self.IsNull?
(JsonValue has a property IsNull.)
February 16, 2018 at 8:51 am #13665If we declare new sub-class JsonNull extends JsonValue and replace current NullValue with them – will it helps us to check if x is null-value w/o any stack-ish problems?
February 17, 2018 at 5:56 am #13667Yes, adding a JsonNull class is probably a better way to do the null value thing, and would prevent at least that particular stack explosion. Are there any others?
I’m still a bit weird about comparing references in fancy ways, just been bitten by it so many times in c++ perhaps. I probably just need to go ahead and do it?
Also in your code you check y=NullValue and return 1 if true, but x can be null itself and then we should get zero in result.
Where in the code? There is a check in the ‘root’ CompareJon for when x and y are both null and it returns 0 there. This is the only place where x can be null, and in all other cases, x is always > null.
The little RndJson test seems to work OK too.
 - 
		AuthorPosts
 
You must be logged in to reply to this topic.