在JSON.NET特定对象的自定义转换自定义、对象、JSON、NET

2023-09-02 01:54:41 作者:别逞强

我用JSON.NET序列化一些我的对象,我想知道是否有一个简单的方法来替代默认json.net转换器只针对特定对象?

I'm using JSON.NET to serialize some of my objects, and i'd like to know if there is a simple way to override the default json.net converter only for a specific object?

目前我有以下类:

public class ChannelContext : IDataContext
{
    public int Id { get; set; }
    public string Name { get; set; }
    public IEnumerable<INewsItem> Items { get; set; }
}

JSON.NET目前连载上述这样的:

JSON.NET currently serializes the above like:

{
    "Id": 2,
    "Name": "name value",
    "Items": [ item_data_here ]
}

是否有可能只是为特定类这种方式,而不是对其进行格式化:

Is it possible just for that specific class to format it this way instead:

"Id_2":
{
    "Name": "name value",
    "Items": [ item data here ]
}

我有点新JSON.NET。我在想,如果上面有事情做与编写自定义转换器。我没能找到如何写一个,如果任何人都可以点我出去给特定信号源的任何具体的例子,我就会非常AP preciate吧。

I'm kinda new to JSON.NET.. I was wondering if the above has something to do with writing a custom converter. I wasn't able to find any concrete examples on how to write one, If anyone can point me out to a specific source, I'll really appreciate it.

我需要找到一个解决方案,这使得该特定阶级总是转换成相同的,因为上述情况下是一个更大的背景下该JSON.NET默认的转换器将只是罚款的一部分。

I need to find a solution which makes that specific class always convert the same, because the above context is a part of an even bigger context which the JSON.NET default converter converts just fine.

希望我的问题是不够清楚......

Hope my question is clear enough...

更新:

我已经找到了如何创建新的自定义转换器(通过创建一个新的类继承自JsonConverter并重写它的抽象方法),我重写了WriteJson方法如下:

I've found how to create a new custom converter (by creating a new class which inherits from JsonConverter and override it's abstract methods), I overriden the WriteJson method as follows:

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        ChannelContext contextObj = value as ChannelContext;

        writer.WriteStartObject();
        writer.WritePropertyName("id_" + contextObj.Id);
        writer.WriteStartObject();
        writer.WritePropertyName("Name");
        serializer.Serialize(writer, contextObj.Name);

        writer.WritePropertyName("Items");
        serializer.Serialize(writer, contextObj.Items);
        writer.WriteEndObject();
        writer.WriteEndObject();
    }

这的确是做这项工作成功,但... 我很好奇,如果有代替手工写作的方式来通过重用默认JsonSerializer(或转换为此事)对象属性的其余部分序列使用jsonwriter方法​​的对象。

This indeed does the job successfully, but... I'm intrigued if there's a way to serialize the rest of the object properties by reusing the default JsonSerializer (or converter for that matter) instead of manually "Writing" the object using the jsonwriter methods.

更新2: 我试图得到一个比较通用的解决方案,并提出了以下内容:

UPDATE 2: I'm trying to get a more generic solution and came up with the following:

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        writer.WriteStartObject();

        // Write associative array field name
        writer.WritePropertyName(m_FieldNameResolver.ResolveFieldName(value));

        // Remove this converter from serializer converters collection
        serializer.Converters.Remove(this);

        // Serialize the object data using the rest of the converters
        serializer.Serialize(writer, value);

        writer.WriteEndObject();
    }

这手动添加时,转换器的串行器,这样能正常工作:

This works fine when adding the converter manually to the serializer, like this:

jsonSerializer.Converters.Add(new AssociativeArraysConverter<DefaultFieldNameResolver>());
jsonSerializer.Serialize(writer, channelContextObj);

但使用时不能正常工作[JsonConverter()]属性设置,因为在执行时会出现一个自我循环引用到我的自定义coverter的ChannelContext级以上:

But doesn't work when using the [JsonConverter()] attribute set to my custom coverter above the ChannelContext class because of a self reference loop that occurs when executing:

serializer.Serialize(writer, value)

这显然是因为我的自定义转换器现在被认为是默认的转换器类,一旦它被设置与JsonConverterAttribute,所以我得到一个inifinite循环。 我唯一​​能想到的,为了解决这个问题,从一个基本的,jsonconverter类继承,并调用base.serialize()方法,而不是... 但就是这样一个JsonConverter类甚至存在?

This is obviously because my custom converter is now considered the default converter for the class once it is set with the JsonConverterAttribute, so I get an inifinite loop. The only thing I can think of, in order to solve this problem is inheriting from a basic, jsonconverter class, and calling the base.serialize() method instead... But is such a JsonConverter class even exists?

非常感谢!

米奇

推荐答案

如果任何人的兴趣在我的解决方案:

If anyone's interested in my solution:

在序列化某些藏品,我想创造一个关联的JSON数组,而不是一个标准的JSON数组,所以我的同事客户端开发人员可以有效地达到,而不是通过他们的迭代这些领域,用自己的名字(或密钥为此事)

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.

考虑以下几点:

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; }
}

响应上下文包含被写入返回给客户端,就像你可以看到整个的反应,它包括一个被称为渠道部分,而不是输出channelcontexts在一个正常的阵列,我希望能以下列方式输出:

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 ]
}
}

因为我想用上述的其他情况下为好,我可能会决定使用不同的属性作为自己的钥匙,甚至可以选择创建自己的独特的名称,它不必做任何财产,我需要某种形式的通用的解决方案,所以我写了一个泛型类被称为AssociativeArraysConverter,它继承自JsonConverter以下列方式:

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();
    }
}

和声明如下界面:

public interface IAssociateFieldNameResolver
{
    string ResolveFieldName(object i_Object);
}

现在所有剩下要做的,就是创建一个实现IAssociateFieldNameResolver的单一功能,它接受的集合中的每一项类,并返回基于该对象,将作为该项目的关联对象的键上的绳子。

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.

例这样的类:

public class ChannelContextFieldNameResolver : IAssociateFieldNameResolver
{
    public string ResolveFieldName(object i_Object)
    {
        return (i_Object as ChannelContext).NominalId;
    }
}