Search code examples
c#json.netserializationjson.net

How to serialize an IEnumerable like a regular class?


I need to convert an object of Computer type to a json string and write it to a file.

The problem that I'm facing is that, due to Computer implementing IEnumerable interface for purposes that are out of the scope of this question, the resulted json after serialization contains those pieces of data that are provided by my custom enumerator, although I'm looking to get object's properties (like how the ususal serialization happens with regular classes).

Here is how it behaves vs how I want it to behave:

Computer class:

class Computer : IEnumerable
{
    public string Identifier { get; set; }
    public string TeamViewerID { get; set; }
    public string TeamViewerPassword { get; set; }
    // and other properties

    public List<CPU> CPUs { get; set; }
    public List<GPU> GPUs { get; set; }
    public List<PSU> PSUs { get; set; }
    // and other collection properties

    // This Enumerator allows me to iterate thorugh Computer's collection properties
    // CPUs, GPUs, PSUs etc. as PropertyInfo
    public IEnumerator GetEnumerator()
    {
        return new DeviceCollectionPropertyEnumerator();
    }
}

Serialization

// computerInstance is of type Computer (duh)
string json = Newtonsoft.Json.JsonConvert.SerializeObject(computerInstance); 

This is the json that I get

[
    {
        "Name": "GPUs",
        "AssemblyName": "SystemInfoCollector, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
        "ClassName": "SystemInfoCollector.Models.Computer",
        "Signature": "System.Collections.Generic.List`1[SystemInfoCollector.Models.GPU] GPUs",
        "Signature2": "System.Collections.Generic.List`1[[SystemInfoCollector.Models.GPU, SystemInfoCollector, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]] GPUs",
        "MemberType": 16,
        "GenericArguments": null
    },
    {
        "Name": "PSUs",
        "AssemblyName": "SystemInfoCollector, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
        "ClassName": "SystemInfoCollector.Models.Computer",
        "Signature": "System.Collections.Generic.List`1[SystemInfoCollector.Models.PSU] PSUs",
        "Signature2": "System.Collections.Generic.List`1[[SystemInfoCollector.Models.PSU, SystemInfoCollector, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]] PSUs",
        "MemberType": 16,
        "GenericArguments": null
    },

    // And the rest of collection properties of Computer type ...
    // This is the stuff that is provided by DeviceCollectionPropertyEnumerator
]

The json that I would love to see (I can get this if I get rid of IEnumerable implementation)

{
    "Identifier": "213231",
    "TeamViewerID": null,
    "TeamViewerPassword": null,
    ...
    "CPUs": [],
    "GPUs": [],
    "PSUs": [],
    ...
}

I guess my custom Enumerator overrides the behaviour of getting properties out of a Computer instance on serialization. How to overcome this? How to serialize this Computer just like any other regular class that does not implement IEnumerator?

I use .NET Framework 4.7.2.


Solution

  • Attach the JsonObjectAttribute to your Computer type:

    [JsonObject]
    class Computer : IEnumerable
    

    This will force serialization to serialize it like an object and not a collection, thus ignoring IEnumerable or IEnumerable<T> altogether.

    Note that this will completely ignore the IEnumerable part so whatever would be serialized through that collection support will either have to be ignored, or handled through those properties as you showed.


    As comments on your question have already stated, it seems like a strange design to make a computer an implicit collection of its components. Since you've stated it is legacy code you're hoping not to disturb too much, and only want to fix the Json.net serialization, you can use that attribute to circumvent the automatic collection serialization.