If anyone's interested in my solution:
When serializing certain collections, I wanted to create an associative json array instead of a standard json array, so my colleague client side developer can reach those fields efficiently, using their name (or key for that matter) instead of iterating through them.
consider the following:
public class ResponseContext
{
private List<ChannelContext> m_Channels;
public ResponseContext()
{
m_Channels = new List<ChannelContext>();
}
public HeaderContext Header { get; set; }
[JsonConverter(
typeof(AssociativeArraysConverter<ChannelContextFieldNameResolver>))]
public List<ChannelContext> Channels
{
get { return m_Channels; }
}
}
[JsonObject(MemberSerialization = MemberSerialization.OptOut)]
public class ChannelContext : IDataContext
{
[JsonIgnore]
public int Id { get; set; }
[JsonIgnore]
public string NominalId { get; set; }
public string Name { get; set; }
public IEnumerable<Item> Items { get; set; }
}
Response context contains the whole response which is written back to the client, like you can see, it includes a section called "channels", And instead of outputting the channelcontexts in a normal array, I'd like to be able to output in the following way:
"Channels"
{
"channelNominalId1":
{
"Name": "name value1"
"Items": [ item data here ]
},
"channelNominalId2":
{
"Name": "name value2"
"Items": [ item data here ]
}
}
Since I wanted to use the above for other contexts as well, and I might decide to use a different property as their "key", or might even choose to create my own unique name, which doesn't have to do with any property, I needed some sort of a generic solution, therefore I wrote a generic class called AssociativeArraysConverter, which inherits from JsonConverter in the following manner:
public class AssociativeArraysConverter<T> : JsonConverter
where T : IAssociateFieldNameResolver, new()
{
private T m_FieldNameResolver;
public AssociativeArraysConverter()
{
m_FieldNameResolver = new T();
}
public override bool CanConvert(Type objectType)
{
return typeof(IEnumerable).IsAssignableFrom(objectType) &&
!typeof(string).IsAssignableFrom(objectType);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
IEnumerable collectionObj = value as IEnumerable;
writer.WriteStartObject();
foreach (object currObj in collectionObj)
{
writer.WritePropertyName(m_FieldNameResolver.ResolveFieldName(currObj));
serializer.Serialize(writer, currObj);
}
writer.WriteEndObject();
}
}
And declared the following Interface:
public interface IAssociateFieldNameResolver
{
string ResolveFieldName(object i_Object);
}
Now all is left to do, is create a class which implements IAssociateFieldNameResolver's single function, which accepts each item in the collection, and returns a string based on that object, which will act as the item's associative object's key.
Example for such a class is:
public class ChannelContextFieldNameResolver : IAssociateFieldNameResolver
{
public string ResolveFieldName(object i_Object)
{
return (i_Object as ChannelContext).NominalId;
}
}
Best Answer
Yes, this is possible using Json.Net 5.0.5 or later. See
JsonConvert.DefaultSettings
.If you're using Web API, you can set up a converter globally like this instead: