public void DictionaryTwoProperty() { var db = DatabaseGenerator.Get(); Product product = new Product() { TypeQuantities = new Dictionary <string, int>() { { "Soft", 1 } } }; var tracker = new DocumentTracker(db); tracker.TrackChanges(product, ObjectUtility.CreateJObject(product, db)); product.TypeQuantities["Soft"] = 5; product.TypeQuantities.Add("Hard", 2); var changed = tracker.GetChanges(product); var expectedJson = JObject.Parse("{'TypeQuantities':{'Hard':2,'Soft':5}}"); Assert.Equal(JObject.DeepEquals(expectedJson, changed), true); }
public async Task RunOnArrayReturnArrayByValue(string eval_fn, string bp_loc, int line, int col, bool roundtrip) => await RunCallFunctionOn(eval_fn, "function () { return Object.getOwnPropertyNames (this); }", "big", bp_loc, line, col, returnByValue : true, roundtrip : roundtrip, test_fn : async(result) => { // Check cfo result AssertEqual("object", result.Value["result"]["type"]?.Value <string>(), "cfo-res-type"); var exp = new JArray(); for (int i = 0; i < 10; i++) { exp.Add(i.ToString()); } exp.Add("length"); var actual = result.Value["result"]?["value"]; if (!JObject.DeepEquals(exp, actual)) { Assert.True(false, $"Results don't match.\nExpected: {exp}\nActual: {actual}"); } await Task.CompletedTask; });
public void ValueSetToNull() { var source = new SimpleModelWithDictionary() { Dic = new Dictionary <string, string>() { { "key1", "val1" } } }; var patched = new SimpleModelWithDictionary() { Dic = new Dictionary <string, string>() { { "key1", null } } }; var diff = DiffBuilder.Build(source, patched); Assert.True(JObject.DeepEquals( JObject.Parse("{Dic:{\"key1\": null}}"), diff)); }
public bool IsConfigSaved() { if (editor.IsChanged()) { return(false); } if (string.IsNullOrEmpty(originalConfig) && string.IsNullOrEmpty(originalFile)) { return(false); } if (string.IsNullOrEmpty(originalFile)) { JObject orgConfig = JObject.Parse(originalConfig); return(JObject.DeepEquals(orgConfig, config)); } JObject orgFile = JObject.Parse(originalFile); return(JObject.DeepEquals(orgFile, config)); }
public void CardTests_AdaptiveCard_BotBuilderAction_AdaptiveBotBuilderAction() { var wrapAction = new CardAction { Type = "messageBack", Value = "some value to bots", Title = "button title", Text = "text posting back to bots", DisplayText = "display text injected in chat stream" }; var action = new AdaptiveBotBuilderAction(wrapAction); var expectedAction = JsonConvert.DeserializeObject(@"{ ""type"": ""Action.Submit"", ""title"": ""button title"", ""data"": { ""msteams"": { ""type"": ""messageBack"", ""value"": ""some value to bots"", ""text"": ""text posting back to bots"", ""displayText"": ""display text injected in chat stream"", } } }"); Assert.IsTrue(JObject.DeepEquals(JObject.FromObject(expectedAction), JObject.FromObject(action))); var card = new AdaptiveCards.AdaptiveCard(); card.Body.Add(new AdaptiveCards.AdaptiveTextBlock()); card.Actions.Add(action); Attachment attachment = card.ToAttachment(); this.TestCard(attachment); }
public void ImportItemList2JObject() { Models.Datas.ImportItem GenItem(bool includeSpeedTest, bool includeActivate, string url, string alias) { return(new Models.Datas.ImportItem { isUseOnActivate = includeActivate, isUseOnSpeedTest = includeSpeedTest, isUseOnPackage = false, url = url, alias = alias, }); } var items = new List <List <Models.Datas.ImportItem> >(); var expects = new List <string>(); items.Add(new List <Models.Datas.ImportItem> { GenItem(true, true, "a.com", "a"), GenItem(false, true, "b.com", "b"), GenItem(true, false, "c.com", ""), }); expects.Add(@"{'v2raygcon':{'import':{'a.com':'a','c.com':''}}}"); items.Add(new List <Models.Datas.ImportItem> { }); expects.Add(@"{'v2raygcon':{'import':{}}}"); for (var i = 0; i < items.Count; i++) { var expect = JObject.Parse(expects[i]); var json = Misc.Utils.ImportItemList2JObject(items[i], true, false, false); var result = JObject.DeepEquals(expect, json); Assert.AreEqual(true, result); } }
public void CardTests_AdaptiveCard_BotBuilderAction_RepresentAsBotBuilderAction() { var wrapAction = new CardAction { Type = "imback", Value = "Text", Title = "button title" }; var action = new AdaptiveCards.AdaptiveSubmitAction(); action.DataJson = @"{""key"": ""value""}"; action.RepresentAsBotBuilderAction(wrapAction); var expectedAction = JsonConvert.DeserializeObject(@"{ ""type"": ""Action.Submit"", ""title"": ""button title"", ""data"": { ""key"": ""value"", ""msteams"": { ""type"": ""imback"", ""value"": ""Text"" } } }"); Assert.IsTrue(JObject.DeepEquals(JObject.FromObject(expectedAction), JObject.FromObject(action))); var card = new AdaptiveCards.AdaptiveCard(); card.Body.Add(new AdaptiveCards.AdaptiveTextBlock()); card.Actions.Add(action); Attachment attachment = card.ToAttachment(); this.TestCard(attachment); }
public void OidAndBytesAreEqual() { byte[] data1 = MiscellaneousUtils.HexToBytes( "82-00-00-00-07-5F-69-64-00-4A-78-93-79-17-22-00-00-00-00-61-CF-04-61-00-5D-00-00-00-01-30-00-00-00-00-00-00-00-F0-3F-01-31-00-00-00-00-00-00-00-00-40-01-32-00-00-00-00-00-00-00-08-40-01-33-00-00-00-00-00-00-00-10-40-01-34-00-00-00-00-00-00-00-14-50-01-35-00-00-00-00-00-00-00-18-40-01-36-00-00-00-00-00-00-00-1C-40-01-37-00-00-00-00-00-00-00-20-40-00-02-62-00-05-00-00-00-74-65-73-74-00-00"); BsonReader reader1 = new BsonReader(new MemoryStream(data1)); reader1.JsonNet35BinaryCompatibility = true; // oid JObject o1 = (JObject)JToken.ReadFrom(reader1); byte[] data2 = MiscellaneousUtils.HexToBytes( "87-00-00-00-05-5F-69-64-00-0C-00-00-00-02-4A-78-93-79-17-22-00-00-00-00-61-CF-04-61-00-5D-00-00-00-01-30-00-00-00-00-00-00-00-F0-3F-01-31-00-00-00-00-00-00-00-00-40-01-32-00-00-00-00-00-00-00-08-40-01-33-00-00-00-00-00-00-00-10-40-01-34-00-00-00-00-00-00-00-14-50-01-35-00-00-00-00-00-00-00-18-40-01-36-00-00-00-00-00-00-00-1C-40-01-37-00-00-00-00-00-00-00-20-40-00-02-62-00-05-00-00-00-74-65-73-74-00-00"); BsonReader reader2 = new BsonReader(new MemoryStream(data2)); reader2.JsonNet35BinaryCompatibility = true; // bytes JObject o2 = (JObject)JToken.ReadFrom(reader2); Assert.IsTrue(o1.DeepEquals(o2)); }
public async Task FixupNameValueObjectsWithMissingParts() { var bp1_res = await SetBreakpointInMethod("debugger-test.dll", "Math", "IntAdd", 3); var names = new JObject[] { JObject.FromObject(new { name = "Abc" }), JObject.FromObject(new { name = "Def" }), JObject.FromObject(new { name = "Xyz" }) }; var values = new JObject[] { JObject.FromObject(new { value = TObject("testclass") }), JObject.FromObject(new { value = TString("test string") }), }; var getters = new JObject[] { GetterRes("xyz"), GetterRes("unattached") }; var list = new[] { names[0], names[1], values[0], names[2], getters[0], getters[1] }; var res = await cli.SendCommand($"Runtime.evaluate", JObject.FromObject(new { expression = $"MONO._fixup_name_value_objects({JsonConvert.SerializeObject(list)})", returnByValue = true }), token); Assert.True(res.IsOk); await CheckProps(res.Value["result"]["value"], new { Abc = TSymbol("<unreadable value>"), Def = TObject("testclass"), Xyz = TGetter("xyz") }, "#1", num_fields : 4); JObject.DeepEquals(getters[1], res.Value["result"]["value"].Values <JObject>().ToArray()[3]);
// Events that we want to update as soon as possible. Return next time this should be called. private int SendFastRateEvents() { if (reset_notify_state_) { notify_state_ = new NotifyState(); } reset_notify_state_ = false; bool game_exists = ffxiv_.FindProcess(); if (game_exists != notify_state_.game_exists) { notify_state_.game_exists = game_exists; DispatchToJS(new JSEvents.GameExistsEvent(game_exists)); } bool game_active = game_active = ffxiv_.IsActive(); if (game_active != notify_state_.game_active) { notify_state_.game_active = game_active; DispatchToJS(new JSEvents.GameActiveChangedEvent(game_active)); } // Silently stop sending other messages if the ffxiv process isn't around. if (!game_exists) { return(kUberSlowTimerMilli); } // onInCombatChangedEvent: Fires when entering or leaving combat. bool in_act_combat = ActGlobals.oFormActMain.InCombat; bool in_game_combat = ffxiv_.GetInGameCombat(); if (!notify_state_.in_act_combat.HasValue || in_act_combat != notify_state_.in_act_combat || !notify_state_.in_game_combat.HasValue || in_game_combat != notify_state_.in_game_combat) { notify_state_.in_act_combat = in_act_combat; notify_state_.in_game_combat = in_game_combat; DispatchToJS(new JSEvents.InCombatChangedEvent(in_act_combat, in_game_combat)); } // onZoneChangedEvent: Fires when the player changes their current zone. string zone_name = ActGlobals.oFormActMain.CurrentZone; if (notify_state_.zone_name == null || !zone_name.Equals(notify_state_.zone_name)) { notify_state_.zone_name = zone_name; DispatchToJS(new JSEvents.ZoneChangedEvent(zone_name)); ClearFateWatcherDictionaries(); } DateTime now = DateTime.Now; // The |player| can be null, such as during a zone change. FFXIVProcess.EntityData player = ffxiv_.GetSelfData(); // onPlayerDiedEvent: Fires when the player dies. All buffs/debuffs are // lost. if (player != null) { bool dead = player.hp == 0; if (dead != notify_state_.dead) { notify_state_.dead = dead; if (dead) { DispatchToJS(new JSEvents.PlayerDiedEvent()); } } } // onPlayerChangedEvent: Fires when current player data changes. if (player != null) { bool send = false; if (!player.Equals(notify_state_.player)) { // Clear the FATE dictionary if we switched characters if (notify_state_.player != null && !player.name.Equals(notify_state_.player.name)) { ClearFateWatcherDictionaries(); } notify_state_.player = player; send = true; } var job = ffxiv_.GetJobSpecificData(player.job); if (job != null) { if (send || !JObject.DeepEquals(job, notify_state_.job_data)) { notify_state_.job_data = job; var ev = new JSEvents.PlayerChangedEvent(player); ev.jobDetail = job; DispatchToJS(ev); } } else if (send) { // No job-specific data. DispatchToJS(new JSEvents.PlayerChangedEvent(player)); } } // onLogEvent: Fires when new combat log events from FFXIV are available. This fires after any // more specific events, some of which may involve parsing the logs as well. List <string> logs; List <string> import_logs; log_lines_semaphore_.Wait(); logs = log_lines_; log_lines_ = last_log_lines_; import_logs = import_log_lines_; import_log_lines_ = last_import_log_lines_; log_lines_semaphore_.Release(); if (logs.Count > 0) { DispatchToJS(new JSEvents.LogEvent(logs)); logs.Clear(); } last_log_lines_ = logs; last_import_log_lines_ = import_logs; return(game_active ? kFastTimerMilli : kSlowTimerMilli); }
// Events that we want to update as soon as possible. Return next time this should be called. private int SendFastRateEvents() { if (reset_notify_state_) { notify_state_ = new NotifyState(); } reset_notify_state_ = false; // Loading dance: // * OverlayPlugin loads addons and initializes event sources. // * OverlayPlugin loads its configuration. // * Event sources are told to load their configuration and start (LoadConfig and Start are called). // * Overlays are initialised and the browser instances are started. At this points the overlays start loading. // * At some point the overlay's JavaScript is executed and OverlayPluginApi is injected. This order isn't // deterministic and depends on what the ACT process is doing at that point in time. During startup the // OverlayPluginApi is usually injected after the overlay is done loading while an overlay that's reloaded or // loaded later on will see the OverlayPluginApi before the page has loaded. // * The overlay JavaScript sets up the initial event handlers and calls the cactbotLoadUser handler through // getUserConfigLocation. These actions are queued by the JS implementation in common.js until OverlayPluginApi // (or the WebSocket) is available. Once it is, the event subscriptions and handler calls are transmitted. // * OverlayPlugin stores the event subscriptions and executes the C# handlers which in this case means // FetchUserFiles is called. That method loads the user files and returns them. The result is now transmitted // back to the overlay that called the handler and the Promise in JS is resolved with the result. // * getUserConfigLocation processes the received information and calls the passed callback. This constructs the // overlay specific objects and registers additional event handlers. Finally, the cactbotRequestState handler // is called. // * OverlayPlugin processes the new event subscriptions and executes the cactbotRequestState handler. // * The next time SendFastRateEvents is called, it resets notify_state_ (since the previous handler set // reset_notify_state_ to true) which causes it to dispatch all state events again. These events are now // dispatched to all subscribed overlays. However, this means that overlays can receive state events multiple // times during startup. If the user has three Cactbot overlays, all of them will call cactbotRequestState and // thus cause this to happen one to three times depending on their timing. This shouldn't cause any issues but // it's a waste of CPU cycles. // * Since this only happens during startup, it's probably not worth fixing though. Not sure. // * Some overlays behave slightly different from the above explanation. Raidboss for example loads data files // in addition to the listed steps. I think it's even loading them twice since raidboss.js loads the data files // for gTimelineController and popup-text.js requests them again for its own purposes. bool game_exists = ffxiv_.FindProcess(); if (game_exists != notify_state_.game_exists) { notify_state_.game_exists = game_exists; OnGameExists(new JSEvents.GameExistsEvent(game_exists)); } bool game_active = game_active = ffxiv_.IsActive(); if (game_active != notify_state_.game_active) { notify_state_.game_active = game_active; OnGameActiveChanged(new JSEvents.GameActiveChangedEvent(game_active)); } // Silently stop sending other messages if the ffxiv process isn't around. if (!game_exists) { return(kUberSlowTimerMilli); } // onInCombatChangedEvent: Fires when entering or leaving combat. bool in_act_combat = Advanced_Combat_Tracker.ActGlobals.oFormActMain.InCombat; bool in_game_combat = ffxiv_.GetInGameCombat(); if (!notify_state_.in_act_combat.HasValue || in_act_combat != notify_state_.in_act_combat || !notify_state_.in_game_combat.HasValue || in_game_combat != notify_state_.in_game_combat) { notify_state_.in_act_combat = in_act_combat; notify_state_.in_game_combat = in_game_combat; OnInCombatChanged(new JSEvents.InCombatChangedEvent(in_act_combat, in_game_combat)); } // onZoneChangedEvent: Fires when the player changes their current zone. string zone_name = Advanced_Combat_Tracker.ActGlobals.oFormActMain.CurrentZone; if (notify_state_.zone_name == null || !zone_name.Equals(notify_state_.zone_name)) { notify_state_.zone_name = zone_name; OnZoneChanged(new JSEvents.ZoneChangedEvent(zone_name)); ClearFateWatcherDictionaries(); } DateTime now = DateTime.Now; // The |player| can be null, such as during a zone change. FFXIVProcess.EntityData player = ffxiv_.GetSelfData(); // onPlayerDiedEvent: Fires when the player dies. All buffs/debuffs are // lost. if (player != null) { bool dead = player.hp == 0; if (dead != notify_state_.dead) { notify_state_.dead = dead; if (dead) { OnPlayerDied(new JSEvents.PlayerDiedEvent()); } } } // onPlayerChangedEvent: Fires when current player data changes. if (player != null) { bool send = false; if (!player.Equals(notify_state_.player)) { // Clear the FateWatcher dictionaries if we switched characters if (notify_state_.player != null && !player.name.Equals(notify_state_.player.name)) { ClearFateWatcherDictionaries(); } notify_state_.player = player; send = true; } var job = ffxiv_.GetJobSpecificData(player.job); if (job != null) { if (send || !JObject.DeepEquals(job, notify_state_.job_data)) { notify_state_.job_data = job; var e = new JSEvents.PlayerChangedEvent(player); e.jobDetail = job; OnPlayerChanged(e); } } else if (send) { // No job-specific data. OnPlayerChanged(new JSEvents.PlayerChangedEvent(player)); } } // onLogEvent: Fires when new combat log events from FFXIV are available. This fires after any // more specific events, some of which may involve parsing the logs as well. List <string> logs; List <string> import_logs; log_lines_semaphore_.Wait(); logs = log_lines_; log_lines_ = last_log_lines_; import_logs = import_log_lines_; import_log_lines_ = last_import_log_lines_; log_lines_semaphore_.Release(); if (logs.Count > 0) { OnLogsChanged(new JSEvents.LogEvent(logs)); logs.Clear(); } if (import_logs.Count > 0) { OnImportLogsChanged(new JSEvents.ImportLogEvent(import_logs)); import_logs.Clear(); } last_log_lines_ = logs; last_import_log_lines_ = import_logs; return(game_active ? kFastTimerMilli : kSlowTimerMilli); }
public async Task HttpTrigger_Model_Binding() { (JObject req, JObject res) = await MakeModelRequest(Fixture.Host.HttpClient); Assert.True(JObject.DeepEquals(req, res), res.ToString()); }
public void ParseImportTest() { var data = new Dictionary <string, string>(); void kv(string name, string key, string val) { var json = JObject.Parse(@"{}"); if (data.ContainsKey(name)) { json = JObject.Parse(data[name]); } json[key] = val; data[name] = json.ToString(Newtonsoft.Json.Formatting.None); } void import(string name, string url) { var json = JObject.Parse(@"{}"); if (data.ContainsKey(name)) { json = JObject.Parse(data[name]); } var imp = Misc.Utils.GetKey(json, "v2raygcon.import"); if (imp == null || !(imp is JObject)) { json["v2raygcon"] = JObject.Parse(@"{'import':{}}"); } json["v2raygcon"]["import"][url] = ""; data[name] = json.ToString(Newtonsoft.Json.Formatting.None); } List <string> fetcher(List <string> keys) { var result = new List <string>(); foreach (var key in keys) { try { // Debug.WriteLine(key); result.Add(data[key]); } catch { throw new System.Net.WebException(); } } return(result); } bool eq(JObject left, JObject right) { var jleft = left.DeepClone() as JObject; var jright = right.DeepClone() as JObject; jleft["v2raygcon"] = null; jright["v2raygcon"] = null; return(JObject.DeepEquals(jleft, jright)); } JObject parse(string key, int depth = 3) { var config = JObject.Parse(data[key]); return(Misc.Utils.ParseImportRecursively(fetcher, config, depth)); } void check(string expect, string value) { Assert.AreEqual(true, eq(JObject.Parse(expect), parse(value))); } data["base"] = "{'v2raygcon':{}}"; kv("a", "a", "1"); kv("b", "b", "1"); kv("baser", "r", "1"); import("baser", "baser"); import("mixAB", "a"); import("mixAB", "b"); import("mixC", "mixAB"); kv("mixC", "a", "2"); kv("mixC", "c", "1"); import("mixCAb", "mixC"); import("mixCAb", "mixAB"); kv("mixCAb", "c", "2"); import("mixABC", "a"); import("mixABC", "b"); import("mixABC", "mixC"); import("final", "mixAB"); import("final", "mixC"); import("final", "mixCAb"); import("final", "baser"); kv("final", "msg", "omg"); check(@"{'a':'2','b':'1','c':'2','r':'1','msg':'omg'}", "final"); check(@"{'a':'2','b':'1','c':'1'}", "mixABC"); check(@"{'a':'1','b':'1','c':'2'}", "mixCAb"); check(@"{'a':'2','c':'1','b':'1'}", "mixC"); check(@"{'a':'1','b':'1'}", "mixAB"); check(data["base"], "base"); check(data["baser"], "baser"); }
static void Main(string[] args) { Console.ForegroundColor = ConsoleColor.White; Console.WriteLine("JsonCompare v0.1 by Deblokt"); if (args.Length < 1) { Console.WriteLine("Usage: To compare two JSON files \"A\" and \"B\""); Console.WriteLine("Command: \"JsonCompare.exe A.json B.json\""); } else { JObject obj1; JObject obj2; using (StreamReader r1 = new StreamReader(args[0])) { var sb = new StringBuilder(r1.ReadToEnd()); if (sb[0] == '[') { sb.Insert(0, "{'arr':"); sb.Append("}"); } obj1 = JsonConvert.DeserializeObject <JObject>(sb.ToString()); }; using (StreamReader r2 = new StreamReader(args[1])) { var sb = new StringBuilder(r2.ReadToEnd()); if (sb[0] == '[') { sb.Insert(0, "{'arr':"); sb.Append("}"); } obj2 = JsonConvert.DeserializeObject <JObject>(sb.ToString()); }; bool isSame = JObject.DeepEquals(obj1, obj2); Console.WriteLine(string.Format("IsSame: {0}", isSame)); if (!isSame) { var diffBuilder = new InlineDiffBuilder(new Differ()); var diff = diffBuilder.BuildDiffModel(obj1.ToString(), obj2.ToString()); foreach (var line in diff.Lines) { switch (line.Type) { case ChangeType.Inserted: Console.ForegroundColor = ConsoleColor.Green; Console.Write("+ "); Console.WriteLine(line.Text); break; case ChangeType.Deleted: Console.ForegroundColor = ConsoleColor.Red; Console.Write("- "); Console.WriteLine(line.Text); break; default: break; } } } } Console.ForegroundColor = ConsoleColor.White; Console.WriteLine("Press ENTER to exit.."); Console.ReadLine(); }
private void AssertJsonEqual(object expected, object actual) { Assert.IsTrue(JObject.DeepEquals(expected as JObject, actual as JObject)); }
public void Searching() { // hide var client = TestClient.GetInMemoryClient(c => c.DisableDirectStreaming().PrettyJson()); var searchResponse = client.Search <Person>(s => s .Query(q => q .Match(m => m .Field(f => f.Name) .Query("Russ") ) ) .Sort(ss => ss .Descending(f => f.Name.Suffix("keyword")) // <1> Use the keyword subfield on `Name` ) .Aggregations(a => a .Terms("peoples_names", t => t .Field(f => f.Name.Suffix("keyword")) ) ) ); /** */ //json var expected = new { query = new { match = new { name = new { query = "Russ" } } }, sort = new object[] { new JObject { { "name.keyword", new JObject { { "order", "desc" } } } } }, aggs = new { peoples_names = new { terms = new { field = "name.keyword" } } } }; // hide JObject.DeepEquals(JObject.FromObject(expected), JObject.Parse(Encoding.UTF8.GetString(searchResponse.ApiCall.RequestBodyInBytes))).Should().BeTrue(); }
public void DeepEqualsIgnoreOrder() { JObject o1 = new JObject( new JProperty("null", null), new JProperty("integer", 1), new JProperty("string", "string!"), new JProperty("decimal", 0.5m), new JProperty("array", new JArray(1, 2))); Assert.True(o1.DeepEquals(o1)); JObject o2 = new JObject( new JProperty("null", null), new JProperty("string", "string!"), new JProperty("decimal", 0.5m), new JProperty("integer", 1), new JProperty("array", new JArray(1, 2))); Assert.True(o1.DeepEquals(o2)); JObject o3 = new JObject( new JProperty("null", null), new JProperty("string", "string!"), new JProperty("decimal", 0.5m), new JProperty("integer", 2), new JProperty("array", new JArray(1, 2))); Assert.False(o1.DeepEquals(o3)); JObject o4 = new JObject( new JProperty("null", null), new JProperty("string", "string!"), new JProperty("decimal", 0.5m), new JProperty("integer", 1), new JProperty("array", new JArray(2, 1))); Assert.False(o1.DeepEquals(o4)); JObject o5 = new JObject( new JProperty("null", null), new JProperty("string", "string!"), new JProperty("decimal", 0.5m), new JProperty("integer", 1)); Assert.False(o1.DeepEquals(o5)); Assert.False(o1.DeepEquals(null)); }
public bool Equals(BodyRule other) { return(other != null && this.Type == other.Type && JObject.DeepEquals(this.RuleValue, other.RuleValue)); }
private static ObjectDiffPatchResult DiffField(string fieldName, JToken source, JToken target, ObjectDiffPatchResult result = null) { if (result == null) { result = new ObjectDiffPatchResult(); } if (source == null) { if (target != null) { AddToken(result, fieldName, source, target); } } else if (target == null) { AddToken(result, fieldName, source, target); } else if (source.Type == JTokenType.Object) { var v = target as JObject; var r = Diff(source as JObject, v); if (!r.AreEqual) { AddToken(result, fieldName, r); } } else if (source.Type == JTokenType.Array) { var aS = (source as JArray); var aT = (target as JArray); if (aS == null || aT == null) { AddToken(result, fieldName, source, target); } else if ((aS.Count == 0 || aT.Count == 0) && (aS.Count != aT.Count)) { AddToken(result, fieldName, source, target); } else { ObjectDiffPatchResult arrayDiff = new ObjectDiffPatchResult(); int minCount = Math.Min(aS.Count, aT.Count); for (int i = 0; i < Math.Max(aS.Count, aT.Count); i++) { if (i < minCount) { DiffField(i.ToString(), aS[i], aT[i], arrayDiff); } else if (i >= aS.Count) { AddNewValuesToken(arrayDiff, aT[i], i.ToString()); } else { AddOldValuesToken(arrayDiff, aS[i], i.ToString()); } } if (!arrayDiff.AreEqual) { if (aS.Count != aT.Count) { AddToken(arrayDiff, PREFIX_ARRAY_SIZE, aS.Count, aT.Count); } AddToken(result, fieldName, arrayDiff); } } } else { if (!JObject.DeepEquals(source, target)) { AddToken(result, fieldName, source, target); } } return(result); }
public async void ShouldDecodeDefaultArrayAndConvertToJObjectUsingABIStringSignature() { /* * struct PurchaseOrder * { * uint256 id; * LineItem[] lineItem; * uint256 customerId; * } * * struct LineItem * { * uint256 id; * uint256 productId; * uint256 quantity; * string description; * }*/ var web3 = _ethereumClientIntegrationFixture.GetWeb3(); var deploymentReceipt = await web3.Eth.GetContractDeploymentHandler <TestInternalDynamicArrayOfDynamicStructs.StructsSample2Deployment>() .SendRequestAndWaitForReceiptAsync().ConfigureAwait(false); var abiLineItem = "tuple(uint256 id, uint256 productId, uint256 quantity, string description)"; var abiPurchaseOrder = $"tuple(uint256 id,{abiLineItem}[] lineItem, uint256 customerId)"; var abi = $@" function GetPurchaseOrder3() public view returns({abiPurchaseOrder}[] purchaseOrder)"; var contract = web3.Eth.GetContract(abi, deploymentReceipt.ContractAddress); var functionPO3 = contract.GetFunction("GetPurchaseOrder3"); var result = await functionPO3.CallDecodingToDefaultAsync().ConfigureAwait(false); var expected = JToken.Parse( @"{ ""purchaseOrder"": [ { ""id"": '1', ""lineItem"": [ { ""id"": '1', ""productId"": '100', ""quantity"": '2', ""description"": ""hello1"" }, { ""id"": '2', ""productId"": '200', ""quantity"": '3', ""description"": ""hello2"" }, { ""id"": '3', ""productId"": '300', ""quantity"": '4', ""description"": ""hello3"" } ], ""customerId"": '1000' } ] }"); Assert.True(JObject.DeepEquals(expected, result.ConvertToJObject())); }