Reliably Detect Property Changes in Objects Using Serialization
This article shows how to reliably detect property changes in objects such as images or barcodes using serialization. It demonstrates how to serialize objects to JSON and how to compare the serialized data to detect property changes.

When the user visually changes a property such as the size or location of a TXText
Serializing Objects
By serializing the object before and after the change, we can compare and find the differences in a very efficient way. The JsonSerializerHelper class implements the SerializeObjectToJson public method that uses the .NET Json implementation to serialize the object.
public static string SerializeObjectToJson<T>(T obj)
{
JsonSerializerOptions options = new JsonSerializerOptions
{
WriteIndented = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault,
Converters =
{
new IgnoreIntPtrConverter()
}
};
try
{
string jsonString = JsonSerializer.Serialize(obj, options);
return jsonString;
}
catch (Exception ex)
{
return string.Empty;
}
}
The other methods compare the serialized objects by comparing the values of all keys at all levels. It is important to check the complete object hierarchy to get access to all properties. For example, the TXText
Comparing Barcode Objects
When a barcode object is changed in the dialog, the Changed event is fired. The following code shows how to compare the barcode objects before and after the change:
var curBarcode = textControl1.Barcodes.GetItem();
var curBarcodeJson = JsonSerializerHelper.SerializeObjectToJson(curBarcode);
var dialogResult = textControl1.BarcodeLayoutDialog();
if (dialogResult == DialogResult.OK)
{
var newBarcode = textControl1.Barcodes.GetItem();
var newBarcodeJson = JsonSerializerHelper.SerializeObjectToJson(newBarcode);
var differences = JsonSerializerHelper.CompareSerializedObjects(curBarcodeJson, newBarcodeJson);
foreach (var difference in differences)
{
Debug.WriteLine(difference);
}
}
First, we serialize the barcode object before opening the BarcodeLayoutDialog. After the dialog has been successfully closed, we have the same object and serialize it for comparison. For the following example, we will change the text of the QR code from "A" to "ABC":
In this sample code, we print the differences, which are returned in a list of strings like this:
barcode.text: A != ABC
barcode.upperTextLength: 1 != 3
You can see that the property Barcode.Text and Barcode.UpperTextLength has been updated by the dialog.
JsonSerializerHelper Implementation
Here is the full implementation of the JsonSerializerHelper class:
using System.Text.Json;
using System.Text.Json.Serialization;
public class JsonSerializerHelper
{
public static string SerializeObjectToJson<T>(T obj)
{
JsonSerializerOptions options = new JsonSerializerOptions
{
WriteIndented = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault,
Converters =
{
new IgnoreIntPtrConverter()
}
};
try
{
string jsonString = JsonSerializer.Serialize(obj, options);
return jsonString;
}
catch (Exception ex)
{
return string.Empty;
}
}
private class IgnoreIntPtrConverter : JsonConverter<IntPtr>
{
public override IntPtr Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
public override void Write(Utf8JsonWriter writer, IntPtr value, JsonSerializerOptions options)
{
writer.WriteNullValue();
}
}
public static List<string> CompareSerializedObjects(string json1, string json2)
{
var differences = new List<string>();
try
{
var dictionary1 = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(json1);
var dictionary2 = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(json2);
foreach (var key in dictionary1.Keys)
{
if (dictionary2.ContainsKey(key))
{
var value1 = dictionary1[key];
var value2 = dictionary2[key];
if (value1.ValueKind == JsonValueKind.Object && value2.ValueKind == JsonValueKind.Object)
{
var subDict1 = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(value1.GetRawText());
var subDict2 = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(value2.GetRawText());
CompareDictionaries(subDict1, subDict2, differences, key);
}
}
else
{
differences.Add($"{key}: {dictionary1[key]} != null");
}
}
foreach (var key in dictionary2.Keys)
{
if (!dictionary1.ContainsKey(key))
{
differences.Add($"{key}: null != {dictionary2[key]}");
}
}
}
catch (Exception ex)
{
// exception handling
}
return differences;
}
private static void CompareDictionaries(Dictionary<string, JsonElement> dict1, Dictionary<string, JsonElement> dict2, List<string> differences, string parentKey)
{
foreach (var key in dict1.Keys)
{
var fullKey = $"{parentKey}.{key}";
if (!dict2.ContainsKey(key))
{
differences.Add($"{fullKey}: {dict1[key]} != null");
}
else if (!JsonElementEquals(dict1[key], dict2[key]))
{
differences.Add($"{fullKey}: {dict1[key]} != {dict2[key]}");
}
}
foreach (var key in dict2.Keys)
{
if (!dict1.ContainsKey(key))
{
var fullKey = $"{parentKey}.{key}";
differences.Add($"{fullKey}: null != {dict2[key]}");
}
}
}
private static bool JsonElementEquals(JsonElement element1, JsonElement element2)
{
if (element1.ValueKind != element2.ValueKind)
{
return false;
}
switch (element1.ValueKind)
{
case JsonValueKind.Object:
var dict1 = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(element1.GetRawText());
var dict2 = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(element2.GetRawText());
return DictionariesEqual(dict1, dict2);
case JsonValueKind.Array:
if (element1.GetArrayLength() != element2.GetArrayLength())
{
return false;
}
var enumerator1 = element1.EnumerateArray();
var enumerator2 = element2.EnumerateArray();
while (enumerator1.MoveNext() && enumerator2.MoveNext())
{
if (!JsonElementEquals(enumerator1.Current, enumerator2.Current))
{
return false;
}
}
return true;
default:
return element1.ToString() == element2.ToString();
}
}
private static bool DictionariesEqual(Dictionary<string, JsonElement> dict1, Dictionary<string, JsonElement> dict2)
{
if (dict1.Count != dict2.Count)
{
return false;
}
foreach (var kvp in dict1)
{
if (!dict2.ContainsKey(kvp.Key) || !JsonElementEquals(kvp.Value, dict2[kvp.Key]))
{
return false;
}
}
return true;
}
}
Conclusion
By serializing objects before and after a change, you can easily compare the differences. This is a very efficient way to detect changes in complex objects such as barcode objects in TX Text Control.
Windows Forms
Text Control combines the power of a reporting tool and an easy-to-use WYSIWYG word processor - fully programmable and embeddable in your Windows Forms application. TX Text Control .NET for Windows Forms is a royalty-free, fully programmable rich edit control that offers developers a broad range of word processing features in a reusable component for Visual Studio.
Related Posts
TX Text Control 33.0 SP3 is Now Available: What's New in the Latest Version
TX Text Control 33.0 Service Pack 3 is now available, offering important updates and bug fixes for all platforms. If you use TX Text Control in your document processing applications, this service…
TX Text Control 33.0 SP2 is Now Available: What's New in the Latest Version
TX Text Control 33.0 Service Pack 2 is now available, offering important updates and bug fixes for all platforms. If you use TX Text Control in your document processing applications, this service…
Document Lifecycle Optimization: Leveraging TX Text Control's Internal Format
Maintaining the integrity and functionality of documents throughout their lifecycle is paramount. TX Text Control provides a robust ecosystem that focuses on preserving documents in their internal…
Expert Implementation Services for Legacy System Modernization
We are happy to officially announce our partnership with Quality Bytes, a specialized integration company with extensive experience in modernizing legacy systems with TX Text Control technologies.
Service Pack Releases: What's New in TX Text Control 33.0 SP1 and 32.0 SP5
TX Text Control 33.0 Service Pack 1 and TX Text Control 32.0 Service Pack 5 have been released, providing important updates and bug fixes across platforms. These service packs improve the…