question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

question: how to instantiate a different type during serializing (or, is IDictionary<k,v> bugged?)

See original GitHub issue

First: HI! This library is amazing. Thank you for working on it.

I -think- I have a likely a documentation/understanding question, but, there’s a small chance this might be a bug.

If I use a regular Dictionary<somekey,somevalue> class, serialization works great.

ISSUE: When I tried to use a third-party dictionary class, I was seeing exceptions with an invalid attempt to cast to IDictionary

Here is a test reproducing the issue with a type called DictionaryExtended that inherits from IDictionary<TKey, TValue>

using System.Collections;
using System.Collections.Generic;
using ExtendedXmlSerializer.Configuration;
using ExtendedXmlSerializer.Tests.ReportedIssues.Support;
using Xunit;

namespace ExtendedXmlSerializer.Tests.ReportedIssues
{
	public sealed class DictionarySerializerNewIssue
	{
		[Fact]
		public void Verify()
		{
			var serializer = new ConfigurationContainer()
				.Create().ForTesting();

                        // my custom IDictionary<k,v> based type
			var testInstance = new DictionaryExtended {
				[0] = "Test0",
				[1] = "Test1",
				[2] = "Test2"
			};
			
			// this line throws
			serializer.Serialize(testInstance);
			// TODO    .Should().Be(@"some-xml");
		}

		[Fact]
		public void Configuration()
		{
			var serializer = new ConfigurationContainer()
				.Create()
				.ForTesting();
		}

		// Custom simple dictionary class.  The key thing seems to be: 
                // this class inherits from IDictionary<int,string> 
                // BUT it does NOT inherit from the non-generic IDictionary (the one with no types attached)
                //
                // this class was created minimally by deriving from IDictionary<int,string>
		// right click -> implement missing members by delegating to new object (dictionary).
		// it creates a bunch of members to implement to the interface
		// then add the initializer for the _dict
		public class DictionaryExtended : IDictionary<int, string>
		{
			IDictionary<int, string> _dict = new Dictionary<int, string>();

			public IEnumerator<KeyValuePair<int, string>> GetEnumerator()
			{
				return _dict.GetEnumerator();
			}

			IEnumerator IEnumerable.GetEnumerator()
			{
				return ((IEnumerable) _dict).GetEnumerator();
			}

			public void Add(KeyValuePair<int, string> item)
			{
				_dict.Add(item);
			}

			public void Clear()
			{
				_dict.Clear();
			}

			public bool Contains(KeyValuePair<int, string> item)
			{
				return _dict.Contains(item);
			}

			public void CopyTo(KeyValuePair<int, string>[] array, int arrayIndex)
			{
				_dict.CopyTo(array, arrayIndex);
			}

			public bool Remove(KeyValuePair<int, string> item)
			{
				return _dict.Remove(item);
			}

			public int Count => _dict.Count;

			public bool IsReadOnly => _dict.IsReadOnly;

			public bool ContainsKey(int key)
			{
				return _dict.ContainsKey(key);
			}

			public void Add(int key, string value)
			{
				_dict.Add(key, value);
			}

			public bool Remove(int key)
			{
				return _dict.Remove(key);
			}

			public bool TryGetValue(int key, out string value)
			{
				return _dict.TryGetValue(key, out value);
			}

			public string this[int key]
			{
				get => _dict[key];
				set => _dict[key] = value;
			}

			public ICollection<int> Keys => _dict.Keys;

			public ICollection<string> Values => _dict.Values;
		}
	}
}

I expect that to work, but, I see the following exception when I run it:

ExtendedXmlSerializer.Tests.ReportedIssues.DictionarySerializerNewIssue.Verify

System.InvalidCastException: Unable to cast object of type 'DictionaryExtended' to type 'System.Collections.IDictionary'.

System.InvalidCastException
Unable to cast object of type 'DictionaryExtended' to type 'System.Collections.IDictionary'.
   at ExtendedXmlSerializer.Tests.ReportedIssues.DictionarySerializerNewIssue.Verify() in D:\projects\extendedxmlserializer\test\ExtendedXmlSerializer.Tests.ReportedIssues\DictSerializeIssue.cs:line 25

ExtendedXmlSerializer is rightfully complaining, this type can’t be cast to IDictionary. For instance, if I run the same cast manually, it fails:

(IDictionary)testInstance;

That cast is happening inside ExtendedXmlSerializer at DictionaryEnumerators.cs:17

public IEnumerator Get(IEnumerable parameter) => (IEnumerator ((IDictionary)parameter)?.GetEnumerator() ?? _entries.GetEnumerator();

the key part there that fails is when it tries this cast:

(IDictionary)parameter

full stack trace below (this is from a slightly different project but same result)

System.InvalidCastException
   at ExtendedXmlSerializer.ReflectionModel.DictionaryEnumerators.Get(IEnumerable parameter) in C:\projects\extendedxmlserializer\src\ExtendedXmlSerializer\ReflectionModel\DictionaryEnumerators.cs:line 17
   at ExtendedXmlSerializer.ContentModel.Members.InstanceMemberWalkerBase`1.<Enumerate>d__5.MoveNext()
   at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
   at System.Linq.Enumerable.<SelectManyIterator>d__17`2.MoveNext()
   at System.Linq.Enumerable.<ConcatIterator>d__59`1.MoveNext()
   at System.Linq.Enumerable.<SelectManyIterator>d__17`2.MoveNext()
   at System.Linq.Enumerable.<DistinctIterator>d__64`1.MoveNext()
   at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
   at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
   at System.Collections.Immutable.ImmutableArray.CreateRange[T](IEnumerable`1 items)
   at ExtendedXmlSerializer.ExtensionModel.References.ReferenceAwareSerializers.Serializer.Write(IFormatWriter writer, Object instance) in C:\projects\extendedxmlserializer\src\ExtendedXmlSerializer\ExtensionModel\References\ReferenceAwareSerializers.cs:line 60
   at ExtendedXmlSerializer.ExtensionModel.Xml.Write.Execute(Writing parameter) in C:\projects\extendedxmlserializer\src\ExtendedXmlSerializer\ExtensionModel\Xml\Write.cs:line 21
   at ExtendedXmlSerializer.ExtensionModel.Xml.Serializer.Serialize(XmlWriter writer, Object instance) in C:\projects\extendedxmlserializer\src\ExtendedXmlSerializer\ExtensionModel\Xml\Serializer.cs:line 19
   at ExtendedXmlSerializer.ExtensionModel.Xml.InstanceFormatter.Get(Object parameter) in C:\projects\extendedxmlserializer\src\ExtendedXmlSerializer\ExtensionModel\Xml\InstanceFormatter.cs:line 33
   at ExtendedXmlSerializer.ExtensionMethodsForSerialization.Serialize(IExtendedXmlSerializer this, XmlWriterSettings settings, Object instance) in C:\projects\extendedxmlserializer\src\ExtendedXmlSerializer\ExtensionMethodsForSerialization.cs:line 43
   at Diz.Test.SerializerTest.Serializer() in D:\projects\src\diztinguish\Diz.Test\SerializerTest.cs:line 66

If I change DictionaryExtended to ALSO inherit from IDictionary, I get a different error about converting keys and values (would need to track it down further to reproduce).


I know I can’t directly cast from IDictionary to IDictionary<TKey, TValue>. I’m looking for advice on best way to work around this in ExtendedXmlSerializer. It feels like not being able to serialize an IDictionary<k,v> derived type is a bug, but, I’m not sure. Or, maybe I messed up something with my enumerators, or maybe this is all a crazy approach. I just want serialized dictionaries 😃

I suspect the new Interceptor approach in #451 is a good way to solve this, but, I couldn’t get it working. That seems like the right way to say ‘hey, anytime you think you should cast to IDictionary for an object of type DictionaryExtended, stop, and instead call my code so I can just create a DictionaryExtended for you, then pick it up from there’.

Either that or, could this be a weird interaction with the serializer not handling the generic params correctly? i.e. it’s trying to create and cast to IDictionary when it should instead be casting to IDictionary<int,string>

And perhaps it’s an issue with ExtendedXmlSerializer thinking it’s OK to cast DictionaryExtended to IDictionary when in fact it’s not actually derived form that interface (just a confusingly similarly named IDictionary<TKey,TValue>)?


Unrelated question [sorry], is there a way to use .TypeOf<>() to select all generic classes? i.e. say I have that same DictionaryExtended<K,V> class and it has a member named Test i’d like to ignore.

Right now it seems I have to type: .TypeOf<DictionaryExtended<int,string>>().Member(x => x.Test).Ignore() i.e. I have to specify every combo of generic params (say, <int,string>,<int,int>,<SomeType,SomeOtherType> etc).

Did I miss a way to just say something like: .TypeNameMatches("DictionaryExtended").Member(x => x.Test).Ignore()

Thanks so much for your time, I know it’s tight right now from your other issue. \m/

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Reactions:1
  • Comments:9 (4 by maintainers)

github_iconTop GitHub Comments

1reaction
binary1230commented, Oct 11, 2020

YESSSS. that does the trick really nicely. thank you so much!

I’ve integrated it into https://github.com/Dotsarecool/DiztinGUIsh/pull/18 and was able to rip out the wrapper stuff. it all seems to be working great!

Thanks again!

1reaction
binary1230commented, Oct 10, 2020

Cool! thanks for looking at it, I really appreciate it.

The project I’m using this for is over here, I actually did end up figuring out how to build a self-registering system for generic types though… it’s probably too hacky for real use. However, it’s a neat proof of concept: https://github.com/binary1230/DiztinGUIsh/blob/master/Diz.Core/util/ObservableDictionaryAdaptor.cs#L18

The key being I use reflection to go through the loaded assemblies, find all the combo of generic types that I want to serialize, and then create a list of functions to apply with ConfigurationContainer. So, effectively, at the start of serialization, I call this on each generic type automatically:

(here, OdWrapper<TKey,Tvalue> is my custom type that I want to apply .Member().Ignore to every instance of it)

        public static IConfigurationContainer AppendDisablingType<TKey, TValue>(this IConfigurationContainer @this)
            => @this
                .EnableImplicitTyping(typeof(OdWrapper<TKey, TValue>))
                .Type<OdWrapper<TKey, TValue>>()
                .Member(x => x.Dict).Ignore();
Read more comments on GitHub >

github_iconTop Results From Across the Web

How can I serialize/deserialize a dictionary with custom ...
This should do the trick: Serialization: JsonConvert.SerializeObject(expected.ToArray(), Formatting.Indented, jsonSerializerSettings);.
Read more >
How do I cast object to dictionary?
Don't: create a method which accepts a generic dictionary<K, V> and K is the type of the key, and V is the type...
Read more >
How do you fix the wrong-case-sensitivity dictionary setting ...
An just use this type as the Directory-Key. This approach works good if you use the Dictionary-Keys in other places as well. So...
Read more >
Finally, a serializable dictionary for Unity! (extracted from ...
Another approach is to use composition instead of inheritance, and let the dictionary object live within the serialized dictionary class ...
Read more >
Dictionary<TKey,TValue> Class
Initializes a new instance of the Dictionary<TKey,TValue> class that contains elements copied from the specified IDictionary<TKey,TValue> and uses the default ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found