1

I am trying to deserialize some YAML that has !!set in a string created by SnakeYaml when serializing Java HashSet. Different generic types are serialized, for example HashSet and with custom type HashSet.

Example YAML:

holidays: !!set
    ? DDMMYYYY: 25/12/2042
      MMDDYYYY: 12/25/2042
      date:
        chronology: &id001
          calendarType: iso8601
          id: ISO
        dayOfMonth: 25
        dayOfWeek: THURSDAY
        dayOfYear: 359
        era: CE
        leapYear: false
        month: DECEMBER
        monthValue: 12
        year: 2042
      serialValue: 52225
    : null

I initially get exception:

Additional information: Could not load file or assembly '2002:set' or one of its dependencies. The system cannot find the file specified.

To fix, I register tag mapping to Deserializer:

{"tag:yaml.org,2002:set", typeof (HashSet<object>)}

I then get exception:

A first chance exception of type 'YamlDotNet.Core.YamlException' occurred in YamlDotNet.dll Additional information: (Line: 4, Col: 23, Idx: 108) - (Line: 5, Col: 9, Idx: 122): Expected 'SequenceStart', got 'MappingStart' (at Line: 4, Col: 23, Idx: 108).

I would have thought handling sets is a very common requirement for YAML, but I can't figure out how to fix this.

Can anyone advise as to how to handle?

Chill
  • 77
  • 11

1 Answers1

1

The problem is that HashSet<T> does not implement IDictionary<TKey, TValue>, and is then deserialized as a sequence instead of a mapping.

You will need to create your own implementation of a set, perhaps by extending HashSet<T> and implementing IDictionary<T, object> like this:

public class YamlSet<T> : HashSet<T>, IDictionary<T, object>
{
    void IDictionary<T, object>.Add(T key, object value)
    {
        Add(key);
    }

    object IDictionary<T, object>.this[T key]
    {
        get
        {
            throw new NotImplementedException();
        }
        set
        {
            Add(key);
        }
    }
    // ...
}

There are more members of IDictionary<T, object> that you will have to implement, but these are the ones that are necessary to make deserialization work.

See a fully working example here

Antoine Aubry
  • 12,203
  • 10
  • 45
  • 74
  • Thanks Antoine. I tried doing that and it gets passed the original exception, but then gives "System.InvalidCastException"..."Object must implement IConvertible." I then changed YamlSet to implement IConvertible. It seems method ToType(Type conversionType, IFormatProvider provider) is required. The conversionType here is System.Collections.ISet of type I was expecting. I've tried various things in the method, such as returning this, converType, but it gives cast exception. Would you be able to explain what should be done here? Would be very much appreciated. – Chill Oct 01 '15 at 08:14
  • You can either make `YamlSet` implement the `ISet` interface, or use a `TypeConverter` to register a converter that can transform `YamlSet` into `ISet`. – Antoine Aubry Oct 02 '15 at 10:28
  • Sorry Antoine, I think I'm missing something... YamlSet already extends HashSet which implements ISet, so I think changing YamlSet to implement ISet does nothing. I tried registering a Type Converter that Accepts{return type == typeof(YamlSet);}, but I'm not sure what to put in ReadYaml. I looked at [this](https://github.com/aaubry/YamlDotNet/blob/master/YamlDotNet.Test/Serialization/SerializationTestHelper.cs) and impemented: public object ReadYaml(IParser parser, Type type){parser.MoveNext();var value = ((Scalar)parser.Current).Value;return new HashSet{value}; } ... – Chill Oct 02 '15 at 12:43
  • ...but I then get an error stating Object must implement IConvertible. Any suggestions? – Chill Oct 02 '15 at 12:44