I have a HashSet<string>
that JsonConvert.SerializeObject
serialises to an array.
When I deserialise using JsonConvert.DeserializeObject<HashSet<string>>
I get a new HashSet<string>
with the same values. However, the Comparer
has been reset.
// JSON settings
var settings = new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
// Create a case insensitive hashset
var h = new HashSet<string>(new string[] {"A", "b"}, StringComparer.OrdinalIgnoreCase);
h.Contains("a"); // TRUE
// Serialise and deserialise with Newtonsoft.Json
string s = JsonConvert.SerializeObject(h, settings);
// s = ["A", "b"]
var newH = JsonConvert.DeserializeObject<HashSet<string>>(s, settings);
// Result is now case-sensitive
newH.Contains("a"); // FALSE
newH.Contains("A"); // TRUE
This is because JsonConvert
uses EqualityComparer<string>.Default
, which is case sensitive.
How do I tell it to use StringComparer.OrdinalIgnoreCase
instead?
I don't want to include the HashSet<string>.Comparer
property in the serialised data (I think it should be a simple array in JSON), I want to specify it at the point of deserialisation.
You could use JsonConvert.PopulateObject()
instead, when the hash set is the root object:
var newH = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
JsonConvert.PopulateObject(s, newH);
When the hash set is not the root object, Json.NET will populate it if preallocated unless ObjectCreationHandling.Replace
is enabled. This allows the containing type to pre-allocate the hash set with the required comparer, e.g.:
public class RootObject
{
public RootObject() { this.Collection = new HashSet<string>(StringComparer.OrdinalIgnoreCase); }
public HashSet<string> Collection { get; private set; }
}
Alternatively, you could subclass CustomCreationConverter<HashSet<T>>
and allocate the hash set with the required comparer within the converter's Create()
method:
public class HashSetCreationConverter<T> : CustomCreationConverter<HashSet<T>>
{
public IEqualityComparer<T> Comparer { get; private set; }
public HashSetCreationConverter(IEqualityComparer<T> comparer)
{
this.Comparer = comparer;
}
public override HashSet<T> Create(Type objectType)
{
return new HashSet<T>(Comparer);
}
}
And then do:
var newH = JsonConvert.DeserializeObject<HashSet<string>>(s, new HashSetCreationConverter<string>(StringComparer.OrdinalIgnoreCase));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With