Browse Source

Add equality/subset checks for JSON data

pull/8/head
Anton Tarasenko 4 years ago
parent
commit
2bb320fa4d
  1. 18
      sources/Data/JSON/JArray.uc
  2. 24
      sources/Data/JSON/JObject.uc
  3. 69
      sources/Data/JSON/JSON.uc
  4. 113
      sources/Data/JSON/Tests/TEST_JSON.uc

18
sources/Data/JSON/JArray.uc

@ -434,6 +434,24 @@ public final function bool RemoveValue(int index, optional int amount)
return true; return true;
} }
public function bool IsSubsetOf(JSON rightValue)
{
local int i;
local JArray rightArray;
local array<JStorageAtom> rightAtomArray;
rightArray = JArray(rightValue);
if (rightArray == none) return false;
if (data.length > rightArray.data.length) return false;
rightAtomArray = rightArray.data;
for (i = 0; i < data.length; i += 1)
{
if (!AreAtomsEqual(data[i], rightAtomArray[i])) {
return false;
}
}
return true;
}
defaultproperties defaultproperties
{ {
} }

24
sources/Data/JSON/JObject.uc

@ -442,6 +442,30 @@ public final function array<string> GetKeys()
return result; return result;
} }
public function bool IsSubsetOf(JSON rightJSON)
{
local int i, j;
local JObject rightObject;
local JProperty rightProperty;
local array<JProperty> nextProperties;
rightObject = JObject(rightJSON);
if (rightObject == none) return false;
for (i = 0; i < hashTable.length; i += 1)
{
nextProperties = hashTable[i].properties;
for (j = 0; j < nextProperties.length; j += 1) {
rightObject.FindProperty(nextProperties[j].name, rightProperty);
if (rightProperty.value.type == JSON_Undefined) {
return false;
}
if (!AreAtomsEqual(nextProperties[j].value, rightProperty.value)) {
return false;
}
}
}
return true;
}
defaultproperties defaultproperties
{ {
ABSOLUTE_LOWER_CAPACITY_LIMIT = 10 ABSOLUTE_LOWER_CAPACITY_LIMIT = 10

69
sources/Data/JSON/JSON.uc

@ -76,6 +76,75 @@ struct JStorageAtom
var protected bool classLoadingWasAttempted; var protected bool classLoadingWasAttempted;
}; };
enum JComparisonResult
{
JCR_Incomparable,
JCR_SubSet,
JCR_Overset,
JCR_Equal
};
public function bool IsSubsetOf(JSON rightJSON)
{
return false;
}
public final function JComparisonResult Compare(JSON rightJSON)
{
local bool firstIsSubset, secondIsSubset;
if (rightJSON == none) return JCR_Incomparable;
firstIsSubset = IsSubsetOf(rightJSON);
secondIsSubset = rightJSON.IsSubsetOf(self);
if (firstIsSubset)
{
if (secondIsSubset) {
return JCR_Equal;
}
else {
return JCR_SubSet;
}
}
else {
if (secondIsSubset) {
return JCR_Overset;
}
else {
return JCR_Incomparable;
}
}
}
public final function bool IsEqual(JSON rightJSON)
{
return (Compare(rightJSON) == JCR_Equal);
}
protected final function bool AreAtomsEqual(
JStorageAtom atom1,
JStorageAtom atom2)
{
if (atom1.type != atom2.type) return false;
if (atom1.type == JSON_Undefined) return true;
if (atom1.type == JSON_Null) return true;
if (atom1.type == JSON_Number) {
return ( atom1.numberValue == atom2.numberValue
&& atom1.numberValueAsInt == atom2.numberValueAsInt);
}
if (atom1.type == JSON_Boolean) {
return (atom1.booleanValue == atom2.booleanValue);
}
if (atom1.type == JSON_String) {
return (atom1.stringValue == atom2.stringValue);
}
if (atom1.complexValue == none && atom2.complexValue == none) {
return true;
}
if (atom1.complexValue == none || atom2.complexValue == none) {
return false;
}
return atom1.complexValue.IsEqual(atom2.complexValue);
}
protected final function TryLoadingStringAsClass(out JStorageAtom atom) protected final function TryLoadingStringAsClass(out JStorageAtom atom)
{ {
if (atom.classLoadingWasAttempted) return; if (atom.classLoadingWasAttempted) return;

113
sources/Data/JSON/Tests/TEST_JSON.uc

@ -28,6 +28,7 @@ protected static function TESTS()
Test_ObjectGetSetRemove(); Test_ObjectGetSetRemove();
Test_ObjectKeys(); Test_ObjectKeys();
Test_ArrayGetSetRemove(); Test_ArrayGetSetRemove();
Test_JSONComparison();
} }
protected static function Test_ObjectGetSetRemove() protected static function Test_ObjectGetSetRemove()
@ -996,6 +997,118 @@ protected static function SubSubTest_ArraySetBooleanExpansions()
TEST_ExpectTrue(testArray.GetTypeOf(6) == JSON_Undefined); TEST_ExpectTrue(testArray.GetTypeOf(6) == JSON_Undefined);
} }
protected static function JObject Prepare_FoldedObject()
{
local JObject testObject;
testObject = _().json.NewObject();
testObject.SetNumber("some_var", 7.32);
testObject.SetString("another_var", "aye!");
testObject.CreateObject("innerObject");
testObject.GetObject("innerObject").SetBoolean("my_bool", true)
.SetInteger("my_int", 9823452).CreateArray("array");
testObject.GetObject("innerObject").GetArray("array").AddClass(class'Actor')
.AddBoolean(false).AddNull().AddObject().AddNumber(56.6);
testObject.GetObject("innerObject").GetArray("array").GetObject(3)
.SetString("something here", "yes").SetNumber("maybe", 0.003);
testObject.GetObject("innerObject").CreateObject("one more");
testObject.GetObject("innerObject").GetObject("one more")
.SetString("o rly?", "ya rly").SetBoolean("whatever", false)
.SetNumber("nope", 324532);
return testObject;
}
protected static function Test_JSONComparison()
{
Context("Testing comparison of JSON objects");
SubTest_JSONIsEqual();
SubTest_JSONIsSubsetOf();
SubTest_JSONCompare();
}
protected static function SubTest_JSONIsEqual()
{
local JObject test1, test2, empty;
test1 = Prepare_FoldedObject();
test2 = Prepare_FoldedObject();
empty = _().json.NewObject();
Issue("`IsEqual()` does not recognize identical JSON objects as equal.");
TEST_ExpectTrue(test1.IsEqual(test1));
TEST_ExpectTrue(test1.IsEqual(test2));
TEST_ExpectTrue(empty.IsEqual(empty));
Issue("`IsEqual()` reports non-empty JSON object as equal to"
@ "an empty one.");
TEST_ExpectFalse(test1.IsEqual(empty));
Issue("`IsEqual()` reports JSON objects with identical variable names,"
@ "but different values as equal.");
test2.GetObject("innerObject").GetObject("one more").SetNumber("nope", 2);
TEST_ExpectFalse(test1.IsEqual(test2));
test2 = Prepare_FoldedObject();
test2.GetObject("innerObject").GetArray("array").SetBoolean(1, true);
TEST_ExpectFalse(test1.IsEqual(test2));
Issue("`IsEqual()` reports JSON objects with different"
@ "structure as equal.");
test2 = Prepare_FoldedObject();
test2.GetObject("innerObject").SetNumber("ahaha", 8);
TEST_ExpectFalse(test1.IsEqual(test2));
test2 = Prepare_FoldedObject();
test2.GetObject("innerObject").GetArray("array").AddNull();
TEST_ExpectFalse(test1.IsEqual(test2));
}
protected static function SubTest_JSONIsSubsetOf()
{
local JObject test1, test2, empty;
test1 = Prepare_FoldedObject();
test2 = Prepare_FoldedObject();
empty = _().json.NewObject();
Issue("`IsSubsetOf()` incorrectly handles equal objects.");
TEST_ExpectTrue(test1.IsSubsetOf(test1));
TEST_ExpectTrue(test1.IsSubsetOf(test2));
TEST_ExpectTrue(empty.IsSubsetOf(empty));
Issue("`IsSubsetOf()` incorrectly handles object subsets.");
test1.SetNumber("Garage", 234);
TEST_ExpectTrue(test2.IsSubsetOf(test1));
TEST_ExpectFalse(test1.IsSubsetOf(test2));
TEST_ExpectTrue(empty.IsSubsetOf(test1));
TEST_ExpectFalse(test1.IsSubsetOf(empty));
Issue("`IsSubsetOf()` incorrectly handles objects that cannot be compared.");
test2.GetObject("innerObject").GetArray("array").SetNull(1);
TEST_ExpectFalse(test1.IsSubsetOf(test2));
TEST_ExpectFalse(test2.IsSubsetOf(test1));
}
protected static function SubTest_JSONCompare()
{
local JObject test1, test2, empty;
test1 = Prepare_FoldedObject();
test2 = Prepare_FoldedObject();
empty = _().json.NewObject();
Issue("`Compare()` incorrectly handles equal objects.");
TEST_ExpectTrue(test1.Compare(test1) == JCR_Equal);
TEST_ExpectTrue(test1.Compare(test2) == JCR_Equal);
TEST_ExpectTrue(empty.Compare(empty) == JCR_Equal);
Issue("`Compare()` incorrectly handles object subsets.");
test1.SetNumber("Garage", 234);
TEST_ExpectTrue(test2.Compare(test1) == JCR_SubSet);
TEST_ExpectTrue(test1.Compare(test2) == JCR_Overset);
TEST_ExpectTrue(empty.Compare(test1) == JCR_SubSet);
TEST_ExpectTrue(test1.Compare(empty) == JCR_Overset);
Issue("`Compare()` incorrectly handles objects that cannot be compared.");
test2.GetObject("innerObject").GetArray("array").AddNull();
TEST_ExpectTrue(test1.Compare(test2) == JCR_Incomparable);
TEST_ExpectTrue(test2.Compare(test1) == JCR_Incomparable);
}
defaultproperties defaultproperties
{ {
caseName = "JSON" caseName = "JSON"

Loading…
Cancel
Save