C# 有没有办法将List的子类JSON.NET序列化<;T>;这也有额外的属性吗?

C# 有没有办法将List的子类JSON.NET序列化<;T>;这也有额外的属性吗?,c#,.net,json,serialization,jsonserializer,C#,.net,Json,Serialization,Jsonserializer,好的,我们正在使用Newtonsoft的JSON.NET产品,我非常喜欢它。不过,我有一个简单的类结构,用于大致如下所示的分层位置 public class Location { public string Name{ get; set; } public LocationList Locations{ get; set; } } // Note: LocationList is simply a subclass of a List<T> // which the

好的,我们正在使用Newtonsoft的JSON.NET产品,我非常喜欢它。不过,我有一个简单的类结构,用于大致如下所示的分层位置

public class Location
{
    public string Name{ get; set; }
    public LocationList Locations{ get; set; }
}

// Note: LocationList is simply a subclass of a List<T>
// which then adds an IsExpanded property for use by the UI.
public class LocationList : List<Location>
{
    public bool IsExpanded{ get; set; }
}

public class RootViewModel
{
    public LocationList RootLocations{ get; set; }
}
现在我也明白了Newtonsoft的产品是可扩展的,因为他们专门讨论了如何为特定数据类型编写自己的自定义序列化程序,这正是我在这里想要的。然而,他们没有任何关于如何做到这一点的好代码示例

如果我们(SO社区)能够解决这个问题,从技术上讲,通过使用上述格式,我们应该能够序列化List的任何子类(或其派生/类似对象),前提是它们还没有名为
Items
的属性(IMHO一开始将是一个糟糕的设计,因为它会被当作垃圾混淆!)也许我们甚至可以让Newtonsoft在其序列化程序中本地运行这样的东西

所以说。。。有人知道如何自定义序列化器/反序列化器以区别对待此对象吗


通常当我发现自己在和这样的事情打交道时,它告诉我我应该考虑另一种方法。在这种情况下,我建议使用以下视图模型结构作为替代:

public class Location
{
    public bool IsExpanded { get; set; }
    public string Name { get; set; }
    public List<Location> Locations { get; set; }
}

public class ViewModel
{
    public List<Location> RootLocations { get; set; }
}
公共类位置
{
公共布尔展开{get;set;}
公共字符串名称{get;set;}
公共列表位置{get;set;}
}
公共类视图模型
{
公共列表根位置{get;set;}
}

好的。。。这就是我想到的。我必须编写自己的JsonConverter。我基本上是用它来创建一个内联JObject,它的属性按照我希望的方式进行结构化,然后我将其持久化。当我把它读出来的时候,我会做相反的事情

然而,缺点是它不使用反射或任何其他类似的东西,因此这只适用于我必须逐个属性地编写代码的特定类型(在本例中只有两个属性,这很好!),而且它也没有利用我必须手动重新模拟的DefaultValues处理,这意味着基本上忽略了属性,除非我对它们进行反思。尽管如此,这仍然有效。完美的没有,但是嘿。。。事情很少发生

当然,欢迎并鼓励您发表评论

public class LocationListJsonConverter : JsonConverter
{
    public override bool CanConvert(System.Type objectType)
    {
        return objectType == typeof(LocationList);
    }

    public override object ReadJson(JsonReader reader, System.Type objectType, object existingValue, JsonSerializer serializer)
    {
        var locationList = (existingValue as LocationList) ?? new LocationList();
        var jLocationList = JObject.ReadFrom(reader);

        locationList.IsExpanded = (bool)(jLocationList["IsExpanded"] ?? false);

        var jLocations = jLocationList["_Items"];
        if(jLocations != null)
        {
            foreach(var jLocation in jLocations)
            {
                var location = serializer.Deserialize<Location>(new JTokenReader(jLocation));
                locationList.Add(location);
            }
        }

        return locationList;

    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var locationList = value as LocationList;

        JObject jLocationList = new JObject();

        if(locationList.IsExpanded)
            jLocationList.Add("IsExpanded", true);

        if(locationList.Count > 0)
        {
            var jLocations = new JArray();

            foreach(var location in locationList)
            {
                jLocations.Add(JObject.FromObject(location, serializer));
            }

            jLocationList.Add("_Items", jLocations);

        }

        jLocationList.WriteTo(writer);

    }

}
公共类位置列表JsonConverter:JsonConverter
{
公共覆盖布尔CanConvert(System.Type objectType)
{
return objectType==typeof(LocationList);
}
公共重写对象ReadJson(JsonReader阅读器、System.Type对象类型、对象existingValue、JsonSerializer序列化程序)
{
var locationList=(现有值为locationList)??新locationList();
var jLocationList=JObject.ReadFrom(读卡器);
locationList.IsExpanded=(bool)(jLocationList[“IsExpanded”]?false);
var jLocations=jLocationList[“_Items”];
如果(jLocations!=null)
{
foreach(jLocations中的var jLocation)
{
var location=serializer.Deserialize(新的JTokenReader(jLocation));
位置列表。添加(位置);
}
}
返回位置列表;
}
公共重写void WriteJson(JsonWriter编写器、对象值、JsonSerializer序列化器)
{
var locationList=作为locationList的值;
JObject jLocationList=新JObject();
if(locationList.IsExpanded)
jLocationList.Add(“IsExpanded”,true);
如果(locationList.Count>0)
{
var jLocations=new JArray();
foreach(locationList中的var位置)
{
添加(JObject.FromObject(位置,序列化程序));
}
jLocationList。添加(“\u项”,jLocations);
}
jLocationList.WriteTo(编写器);
}
}

我需要一个名为FieldGroup的类,它也有一些属性来对一些字段进行分组。我先是这样做的

public class FieldGroup : List<Field>{ ... }
公共类字段组:列表{…}
正如博文所说,它存在序列化的问题所以我修改了下面的类。这样我就可以像处理从列表派生的*FieldGroup类一样处理它了

公共类字段组:IPrintable、IEnumerable
{
public PrintFormat GroupFormat{get;set;}=new PrintFormat();
公共列表字段{get;set;}=new List();
公共字段此[int索引]
{
get=>字段[索引];
set=>字段[索引]=值;
}
公共作废添加(字段)
{
字段。添加(字段);
}
公共IEnumerator GetEnumerator()
{
返回新的FieldEnumerator(字段);
}
IEnumerator IEnumerable.GetEnumerator()
{
返回GetEnumerator();
}
...
}

您使用什么将其序列化为json?json.net、datacontracts…?如果对
IsExpanded
属性使用自动属性表示法,会有什么不同吗?换句话说,
公共bool是扩展的{get;set;}
?我不确定这是否与此有关,但这正是我认为该属性不同的地方。@ataddeini我不认为这是问题所在,因为编译的IL在任何方面都是相同的。唯一的区别是私有字段的名称。我认为更可能的情况是,无论使用什么序列化程序,都会对列表进行优化,并且没有正确地选择LocationList是派生类型。@Harry Steinhilber:是的,我同意。“说得好!”阿塔迪尼,我把它改回来了。一直困扰着我,为什么我在这里这么做,但后来我记得在我复制的代码中,我们使用了成员变量,但为了清晰起见,我去掉了这一部分。我只是忘了把它改成更短的版本,正如我说的,现在我有了。谢谢!:)我想了想,但我觉得这样不好有两个原因。一种是将属性与其应用对象(即列表)分离(因此必须在根V上再次复制它)
public class FieldGroup : List<Field>{ ... }
public class FieldGroup : IPrintable, IEnumerable<Field>
{
    public PrintFormat GroupFormat { get; set; } = new PrintFormat();
    public List<Field> Fields { get; set; } = new List<Field>();

    public Field this[int index]
    {
        get => Fields[index];
        set => Fields[index] = value;
    }

    public void Add(Field field)
    {
        Fields.Add(field);
    }
    public IEnumerator<Field> GetEnumerator()
    {
        return new FieldEnumerator(Fields);
    }
    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    ...
}