当前位置: 首页 > 面试题库 >

如何将json对象反序列化为特定的子类?

薛坚
2023-03-14
问题内容

我有一个Cabin类,其中包含Row对象的列表。我想像这样序列化对象,但是在反序列化时,我希望Row对象是从Row对象继承的RowRule对象。下面是我一直在尝试的一些示例代码

class Program
{
    static void Main(string[] args)
    {
        var cabin = new Cabin();
        var row = new Row();
        row.Status = "Success";
        cabin.Rows = new List<Row>()
        {
            row,
            row
        };

        JsonSerializerSettings settings = new JsonSerializerSettings()
        {
            TypeNameHandling = TypeNameHandling.Auto
        };
        string json = JsonConvert.SerializeObject(cabin, Formatting.Indented, settings);
        Console.WriteLine(json);

        Cabin obj = JsonConvert.DeserializeObject<Cabin>(json,
            new JsonSerializerSettings() {TypeNameHandling = TypeNameHandling.Auto});
        Console.WriteLine(obj);
        Debug.Assert(obj.Rows.First().GetType().Name == "RowRule");
    }
}

class Cabin
{
    public IList<Row> Rows { get; set; } 
}

class Row
{
    public string Status { get; set; }
}

class RowRule : Row
{

}

问题答案:

简单的答案是使用a
CustomCreationConverter<Row>RowRule从返回a
Create()

class RowToRoleRuleConverter : CustomCreationConverter<Row>
{
    public override Row Create(Type objectType)
    {
        if (objectType.IsAssignableFrom(typeof(RowRule)))
            return Activator.CreateInstance<RowRule>();
        return (Row)Activator.CreateInstance(objectType);
    }
}

但是,使用TypeNameHandling.Auto意味着"$type"您的JSON中可能存在多态属性。不幸的是,CustomCreationConverter<T>忽略了这些属性。因此,有必要做一些额外的工作并创建DowncastingConverter<TBase, TDerived>

public class DowncastingConverter<TBase, TDerived> : PolymorphicCreationConverter<TBase> where TDerived : TBase
{
    protected override TBase Create(Type objectType, Type polymorphicType, object existingValue, IContractResolver contractResolver, JObject obj)
    {
        Type createType = objectType;
        if (createType.IsAssignableFrom(polymorphicType))
            createType = polymorphicType;
        if (createType.IsAssignableFrom(typeof(TDerived)))
            createType = typeof(TDerived);

        if (existingValue != null && createType.IsAssignableFrom(existingValue.GetType()))
            return (TBase)existingValue;

        var contract = contractResolver.ResolveContract(createType);
        return (TBase)contract.DefaultCreator();
    }
}

public abstract class PolymorphicCreationConverter<T> : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return typeof(T).IsAssignableFrom(objectType);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotSupportedException("CustomCreationConverter should only be used while deserializing.");
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
            return null;
        var obj = JObject.Load(reader);
        Type polymorphicType = null;

        var polymorphicTypeString = (string)obj["$type"];
        if (polymorphicTypeString != null)
        {
            if (serializer.TypeNameHandling != TypeNameHandling.None)
            {
                string typeName, assemblyName;
                ReflectionUtils.SplitFullyQualifiedTypeName(polymorphicTypeString, out typeName, out assemblyName);
                polymorphicType = serializer.Binder.BindToType(assemblyName, typeName);
            }
            obj.Remove("$type");
        }

        var value = Create(objectType, polymorphicType, existingValue, serializer.ContractResolver, obj);
        if (value == null)
            throw new JsonSerializationException("No object created.");

        using (var subReader = obj.CreateReader())
            serializer.Populate(subReader, value);
        return value;
    }

    protected abstract T Create(Type objectType, Type polymorphicType, object existingValue, IContractResolver iContractResolver, JObject obj);

    public override bool CanWrite { get { return false; } }
}

internal static class ReflectionUtils
{
    // Utilities taken from https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs
    // I couldn't find a way to access these directly.

    public static void SplitFullyQualifiedTypeName(string fullyQualifiedTypeName, out string typeName, out string assemblyName)
    {
        int? assemblyDelimiterIndex = GetAssemblyDelimiterIndex(fullyQualifiedTypeName);

        if (assemblyDelimiterIndex != null)
        {
            typeName = fullyQualifiedTypeName.Substring(0, assemblyDelimiterIndex.GetValueOrDefault()).Trim();
            assemblyName = fullyQualifiedTypeName.Substring(assemblyDelimiterIndex.GetValueOrDefault() + 1, fullyQualifiedTypeName.Length - assemblyDelimiterIndex.GetValueOrDefault() - 1).Trim();
        }
        else
        {
            typeName = fullyQualifiedTypeName;
            assemblyName = null;
        }
    }

    private static int? GetAssemblyDelimiterIndex(string fullyQualifiedTypeName)
    {
        int scope = 0;
        for (int i = 0; i < fullyQualifiedTypeName.Length; i++)
        {
            char current = fullyQualifiedTypeName[i];
            switch (current)
            {
                case '[':
                    scope++;
                    break;
                case ']':
                    scope--;
                    break;
                case ',':
                    if (scope == 0)
                    {
                        return i;
                    }
                    break;
            }
        }

        return null;
    }
}

然后像这样使用它:

JsonSerializerSettings readSettings = new JsonSerializerSettings()
{
    TypeNameHandling = TypeNameHandling.Auto,
    Converters = new[] { new DowncastingConverter<Row, RowRule>() },
};
Cabin obj = JsonConvert.DeserializeObject<Cabin>(json, readSettings);

原型小提琴。

最后,在使用时TypeNameHandling,请注意Newtonsoft文档中的这一警告:

当您的应用程序从外部源反序列化JSON时,应谨慎使用TypeNameHandling。反序列化除None以外的其他值时,应使用自定义SerializationBinder验证传入的类型。



 类似资料:
  • 我使用NewtonSoft.json来反序列化这个json

  • 问题内容: 我在使用AJAX访问的Java服务器应用程序中有一个字符串。它看起来像以下内容: 当从服务器提取字符串时,是否有一种简单的方法可以将其转换为活动的JavaScript对象(或数组)?还是我必须手动拆分字符串并手动构建对象? 问题答案: 现代浏览器支持。 在不浏览器,您可以包括在库中。

  • 我在爪哇是新来的! 我的目标:我有收集的对象,让我保持任何类型的对象通过一些键(如地图)(把他们到它和从)。我可以向它添加任何类型的对象(例如,我可以添加MyClass的对象)。然后,我使用GSON序列化这个对象以JSON格式分离字符串(例如,我可以序列化这个字符串以分离文件)。当我想拿回某个对象时,我通过键从collection请求它,collection将对应的字符串反序列化到object对象

  • 有没有办法将一般对象序列化为Json并将Json反序列化为对象<如果对象是一个实体,我可以使用Jackson 2库来实现这个目的<但是如果对象是一个普通类,我该怎么做呢? 例如,我想序列化com.datastax.driver.core.querybuilder。将更新为Json并保存到DB,然后搜索并反序列化它以更新对象,最后将其用作com.datastax.driver.core.Sessio

  • 问题内容: 我需要将一些对象序列化为JSON并发送到WebService。如何使用org.json库?否则我将不得不使用另一个?这是我需要序列化的类: 我只放了类的变量和构造函数,但也有getter和setter方法。所以如果有人可以帮忙 问题答案: 没有注释的简单方法是使用Gson库 就那么简单:

  • 问题内容: 如何将上述字符串反序列化为java对象。 我正在使用的类是 问题答案: @基达 我假设您可以控制JSON输入字符串的创建方式。我认为JSON字符串格式不正确,无法对地图类型进行默认的GSON反序列化。 我已经修改了输入字符串供您考虑,这将导致非null的LocalLocationId 如果我对输入字符串的假设不正确,请发表评论。 编辑1:由于无法修改输入,请考虑编写自定义解串器。以下是