首先 - 我知道我将在这个特定情况下解决我的问题,我将向您展示(通过保存命名空间,或者在反序列化期间通过自定义反序列化器,如果你理解我的意思),但是对于计划的需求不会出现面向未来的情况,所以请不要提出建议。
我很久以前就开始使用JSON.net了,这是我第一次尝试创建自定义转换器,我无法在网络上的任何地方找到它。我已经使用了几个为类似需求编码的转换器,所以如果我在这里有几个不需要的步骤 - 那么不仅要纠正问题本身,还要告诉我是否有什么可以删除为了性能优势,谢谢。
现在转向代码(我已经删除了所有不相关的东西)。 这是接口实现:
[JsonConverter(typeof(StringEnumConverter))]
public enum SomeInterfaceType
{
NoType = 0,
Type1 = 1,
Type2 = 2,
//...
}
[JsonConverter(typeof(SomeInterfaceConverter))]
public interface SomeInterface
{
SomeInterfaceType StoredType { get; }
//...
}
正如您所看到的,接口存储了一些与接口相关的一般信息,以及StoredType,稍后我们将在反序列化过程中使用它来识别特定的类。
课程本身非常直接:
public class Class1 : SomeInterface
{
public SomeInterfaceType StoredType { get { return SomeInterfaceType.Type1; } }
//...
}
public class Class2 : SomeInterface
{
public SomeInterfaceType StoredType { get { return SomeInterfaceType.Type2; } }
//...
}
//...
有很多这些,它们有很多属性和功能,但这与问题无关,所以这个例子就足够了。
这些类(接口)将存储在程序本身中:
public class LowerClass
{
public Dictionary<SomeKey, List<SomeInterface>> interfaceDictionary;
//...
}
public class MiddleClass
{
private LowerClass ccc;
//...
}
public class TopClass
{
private MiddleClass sss;
//...
}
实现该接口的类数量将随着时间的推移而增加,并且此选择的其他原因很少。 保存命名空间对于这种特殊情况非常适用,但序列化信息可以被各种程序(各种语言)使用,因此它不是最好的解决方案,甚至不提如果有人要手动更改(这应该是可能的),或者如果由于某种原因未来命名空间发生变化,正如我所说 - 不是最好的解决方案。
现在转向问题的核心:
public class SomeInterfaceConverter: JsonConverter
{
public SomeInterfaceConverter() { }
JsonObjectContract FindContract(JObject obj, JsonSerializer serializer)
{
SomeInterfaceType typeQ = obj["StoredType"].ToObject<SomeInterfaceType>();
switch (typeQ)
{
case SomeInterfaceType.Type1:
{
return serializer.ContractResolver.ResolveContract(typeof(Class1)) as JsonObjectContract;
}
case SomeInterfaceType.Type2:
{
return serializer.ContractResolver.ResolveContract(typeof(Class2)) as JsonObjectContract;
}
//...
case SomeInterfaceType.NoType:
default:
{
return null;
}
}
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(SomeInterface);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null) return null;
JObject obj = JObject.Load(reader);
var contract = FindContract(obj, serializer);
if (contract == null) throw new JsonSerializationException("No contract found in " + obj.ToString());
existingValue = contract.DefaultCreator();
using (var sr = obj.CreateReader())
{
serializer.Populate(sr, existingValue);
}
return existingValue;
}
public override bool CanWrite { get { return false; } }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
我不完全确定我是否需要使用这个合同的东西,但我基本上想要省略任何其他类的自定义转换器。此外,我想省略为整个主类做自定义转换器,并希望保留标准转换器,除了实现SomeInterface的类(此时它们只存在于特定的字典中,但是这可能在将来发生变化)同样)。
FindContract完全按预期执行,从json对象读取一切正常,然后可能返回所需的合同? (在网上找到)。 然后在这一行:
existingValue = contract.DefaultCreator();
它抛出NullReferenceException
。合同肯定存在(如果它是正确生产的)。我玩了几个小时,试着在stackoverflow和其他网站上找到不同的解决方法,但无论我怎么做,我仍然坚持这个例外。
正如我从一开始就说过的那样 - 我对JSON.net很新,可能它应该很容易。也许我甚至不需要那个合同,而应该使用DeserializeObject<>
或其他东西(我也看过那个,但找不到足够接近的东西,可以很容易地转移到我的案例中)。
ClassesX和TypesX的所有可能组合都是已知的,并且可以没有任何问题进行硬编码。
提前感谢您花时间和任何有价值的提示/更正。
更新
根据评论中的要求,这里是JSON:
{
"LowerClass": {
"interfaceDictionary": {
"Key1": [
{
"StoredType": "Type1"
},
{
"StoredType": "Type2"
}
]
}
}
}
此外,我决定添加反序列化的方式,以防万一(因为上面的JSON省略了顶级类,而不是它与问题有任何关联):
MiddleClass whatever = JsonConvert.DeserializeObject<MiddleClass>(source);
答案 0 :(得分:0)
我最终挖掘了JSON.net代码,并从我的理解 - 如果有任何方法迫使contract.DefaultCreator()
按预期工作(不抛出NullReferenceException
),那么它将是一个相当的复杂的事情要实现。
我没有试图弄清楚什么是“修复”DefaultCreator的最简单方法,而是简单地创建了一个变通办法并完全取代了它:
//replaced that:
existingValue = contract.DefaultCreator();
//with that:
existingValue = ContractToObject(type, contract);
功能本身:
object ContractToObject(SomeInterfaceType type, JsonObjectContract contract)
{
switch (type)
{
case SomeInterfaceType.Type1:
{
return new Class1(Params);
}
case SomeInterfaceType.Type2:
{
return new Class2(Params);
}
//...
case SomeInterfaceType.None:
default:
{
return null;
}
}
}