Ответ 1
Если кто-то заинтересован в моем решении:
При сериализации определенных коллекций я хотел создать ассоциативный json-массив вместо стандартного json-массива, поэтому мой коллега-клиент-разработчик может эффективно использовать эти поля, используя их имя (или ключ в этом отношении) вместо повторения через них.
рассмотрим следующее:
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; }
}
Контекст ответа содержит весь ответ, который записывается обратно клиенту, как вы можете видеть, он включает в себя раздел, называемый "каналы". Вместо того, чтобы выводить канальные контексты в обычном массиве, я хотел бы иметь возможность вывод следующим образом:
"Channels"
{
"channelNominalId1":
{
"Name": "name value1"
"Items": [ item data here ]
},
"channelNominalId2":
{
"Name": "name value2"
"Items": [ item data here ]
}
}
Так как я хотел использовать вышеприведенное для других контекстов, и я мог бы решить использовать другое свойство в качестве своего "ключа" или даже захотел бы создать свое собственное уникальное имя, которое не имеет отношения к любое свойство, мне понадобилось какое-то общее решение, поэтому я написал общий класс AssociativeArraysConverter, который наследует от JsonConverter следующим образом:
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();
}
}
И объявлен следующий интерфейс:
public interface IAssociateFieldNameResolver
{
string ResolveFieldName(object i_Object);
}
Теперь все осталось сделать, это создать класс, который реализует одиночную функцию IAssociateFieldNameResolver, которая принимает каждый элемент в коллекции и возвращает строку на основе этого объекта, которая будет действовать как элемент ассоциативного объекта элемента.
Пример для такого класса:
public class ChannelContextFieldNameResolver : IAssociateFieldNameResolver
{
public string ResolveFieldName(object i_Object)
{
return (i_Object as ChannelContext).NominalId;
}
}