1

I have a class that is a wrapper over a string providing some additional functionality. Among other interfaces it also implements the IEnumerable<char> interface.

I'd like to be able to serialize it to a string with JsonNet. Because it didn't work and always serialized into an array - I guess because of the IEnumerable<char> interface, I added the ISerializable interface. But for some reason JsonNet still creates an array and ignores ISerializable. Adding the SerializableAttribute didn't help either.

Here's a small proof of concept code that demonstrates the behaviour (for LINQPad):

void Main()
{
    JsonConvert.SerializeObject(new NotACollection("foo")).Dump(); // ["f","o","o"]
}

[Serializable]
class NotACollection : IEnumerable<char>, ISerializable
{
    private readonly string _value;

    public NotACollection(string value)
    {
        _value = value;
    }

    public IEnumerator<char> GetEnumerator()
    {
        return _value.GetEnumerator();
    }   

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

    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue("_value", _value);
    }

    public override string ToString()
    {
        return _value;
    }
}

I know I could create a custom JsonCovert class but since the wrapper is in a library where I don't want to reference JsonNet, I'd prefer another solution.

Am I doing something wrong here? I thought JsonNet would pick the ISerializable implementation if it's available?

(this class must not implement an implicit conversion to a string because this would defeat its purpose and result in weird bugs)

t3chb0t
  • 16,340
  • 13
  • 78
  • 118
  • Only `BinaryFormatter` uses `Serializable` attribute and `ISerializable` interface. They mean nothing to json.net. – Alexander Petrov Feb 04 '18 at 13:23
  • 1
    @AlexanderPetrov mhmm... but it looks like it should, see [ISerializable](https://www.newtonsoft.com/json/help/html/SerializationGuide.htm#ISerializable) - _Types that implement ISerializable are serialized as JSON objects. When serializing, only the values returned from ISerializable.GetObjectData are used; members on the type are ignored. When deserializing, the constructor with a SerializationInfo and StreamingContext is called, passing the JSON object's values_ – t3chb0t Feb 04 '18 at 13:26
  • Hmm, I was wrong. – Alexander Petrov Feb 04 '18 at 13:34
  • 1
    In the next release of Json.NET you should be able to use data contract attributes to override the `IEnumerable` attribute. See [Deserializing an `IEnumerable` with `[DataContract]` applied does not work](https://stackoverflow.com/q/35778811/3744182) which notes that it has recently been implemented. – dbc Feb 04 '18 at 18:14
  • 1
    Oh, I think this is a duplicate: [How to serialize an ISerializable object into SOAP or Json or Xml](https://stackoverflow.com/q/38188639/3744182). Related github issue: [IEnumerable and ISerializable conflict #1094](https://github.com/JamesNK/Newtonsoft.Json/issues/1094). – dbc Feb 05 '18 at 01:04
  • @dbc you're right. I didn't see the other question and marked mine as a duplicate now. Thanks for the links, they explain pretty much everything ;-) – t3chb0t Feb 05 '18 at 05:11

1 Answers1

2

If you add [JsonObject(MemberSerialization.Fields)] attribute for your class you are wanting to serialize, then it should serialize the class field to Json {"_value" : "foo" }

    [JsonObject(MemberSerialization.Fields)]
    internal class NotACollection : IEnumerable<char>, ISerializable
NightOwl888
  • 55,572
  • 24
  • 139
  • 212
Vidmantas Blazevicius
  • 4,652
  • 2
  • 11
  • 30