-2

JsonObjectAttribute can be used to serialize a class that implements IEnumerable< T> as a JSON object instead of a JSON array

A perfect example can be found here.

It works fine with my own defined classes since I have the control. But when i try to write a program to deal with a third party library, I can not add JsonObjectAttribute on those classes.

Is there another way to tell JsonConvert.SerializeObject to do the similar thing like JsonObjectAttribute does?

UPDATE:

Here is the example I copy and paste from Json.net document.

 [JsonObject]
 public class Directory : IEnumerable<string>
 {
     public string Name { get; set; }
     public IList<string> Files { get; set; }

     public Directory()
     {
         Files = new List<string>();
    }

    public IEnumerator<string> GetEnumerator()
    {
        return Files.GetEnumerator();
    }

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

 Directory directory = new Directory
 {
     Name = "My Documents",
     Files =
     {
         "ImportantLegalDocuments.docx",
         "WiseFinancalAdvice.xlsx"
     }
 };

string json = JsonConvert.SerializeObject(directory, Formatting.Indented);

Console.WriteLine(json);
// {
//   "Name": "My Documents",
//   "Files": [
//     "ImportantLegalDocuments.docx",
//     "WiseFinancalAdvice.xlsx"
//   ]
// }

Now, just think of [JsonObject] is not on that class definition, then how can you achieve the same result?

liuhongbo
  • 2,051
  • 4
  • 22
  • 29
  • we can use that mentioned example to show my problem, if that class Directory does not have the [JsonObject] attribute, how can i achieve the same thing? I don't want to use another middle object to fix the issue. – liuhongbo Apr 11 '16 at 20:56
  • Did you try creating a class the inherits from the third party class (unless it's sealed, of course) and decorating it with the attribute? – Rodrigo Vedovato Apr 11 '16 at 21:00
  • That library is a API library, it has hundreds APIs. Each api call return a object of different type, that type is defined in that library, . I want to find uniform way to convert that returned object to json object. So if I have to define the inherits for each different type, which is a lot code i want to void. – liuhongbo Apr 11 '16 at 21:08
  • @L.B updated the question with the code (copy/paste from that link). – liuhongbo Apr 11 '16 at 21:15
  • @L.B with the updated code sample, I think my question is clear enough now. – liuhongbo Apr 11 '16 at 21:18
  • @L.B which part is not clear? – liuhongbo Apr 11 '16 at 21:22
  • the sample class is the same class but without the [JosnObject], the expected output is the same output as the original, the question is, how? – liuhongbo Apr 11 '16 at 21:25

1 Answers1

2

You can make a custom contract resolver that maintains a list of types that should be serialized as objects:

public class ObjectOverrideContractResolver : DefaultContractResolver
{
    readonly HashSet<Type> overrideObjectTypes;

    public ObjectOverrideContractResolver(IEnumerable<Type> overrideObjectTypes)
    {
        if (overrideObjectTypes == null)
            throw new ArgumentNullException();
        this.overrideObjectTypes = new HashSet<Type>(overrideObjectTypes);
    }

    protected override JsonContract CreateContract(Type objectType)
    {
        if (overrideObjectTypes.Contains(objectType))
        {
            var contract = CreateObjectContract(objectType);
            // Mark get-only properties like Count as ignored 
            foreach (var property in contract.Properties)
            {
                if (!property.Writable)
                    property.Ignored = true;
            }
            return contract;
        }
        else
        {
            var contract = base.CreateContract(objectType);
            return contract;
        }
    }
}

Then set it in JsonSerializerSettings.ContractResolver:

var resolver = new ObjectOverrideContractResolver(new[] { typeof(Directory) });
var settings = new JsonSerializerSettings { ContractResolver = resolver };

var json = JsonConvert.SerializeObject(directory, Formatting.Indented, settings);

This produces the output you require. If needed, it could be enhanced to check whether the type or any of its base types are in the hash table if required.

Note that, for best performance, you should cache and reuse contract resolvers.

dbc
  • 104,963
  • 20
  • 228
  • 340