2016-12-01 85 views
1
  • 我创建的JsonConverter
  • CanConvert总是返回true的实现。
  • ReadJson我想有时只使用“默认”行为,就好像CanConvert已返回false,我的ReadJson从未被调用。
  • 各个岗位都建议我做以下一些变化:

如何获得ReadJson返回“默认”的行为 - 好像CanConvert返回false

existingValue = existingValue ?? serializer 
            .ContractResolver 
            .ResolveContract(objectType) 
            .DefaultCreator(); 
serializer.Populate(reader, existingValue); 
  • 但是,这将引发对.DefaultCreator()NullReferenceException
  • existingValue总是null
  • 是从串行器返回的是我自己的ContractResolver。它扩展json.net的内置CamelCasePropertyNamesContractResolver,只是重写方法CreateConstructorParametersCreatePropertyFromConstructorParameter

我如何告诉json.net - “只是在开玩笑,我不知道如何创建这个东西,做任何你会已经完成了创建它,但我告诉过你我无法创建它“

请注意,我简化了讨论的问题。我期待有人会回答“只有CanCreate返回false”事实上,在一些情况下,我可以并应该创建对象。

+0

*事实上,在一些情况下,我可以而且应该创建对象*什么情景?当ReadJson应该处理这个对象的时候,什么时候应该处理这个对象呢? –

+0

我现在意识到我可以将读取(反序列化)和写入(序列化)分离为单独的转换器类,从而解决了我的问题。我原本以为CanConvert必须处理这两种情况。 CanConvert需要在写入端返回true,而在读取端则返回false。因此,我只是返回true(以便写入工作),并且我试图调用ReadJson来忽略它被调用的事实。 – SFun28

回答

1

从您的问题和评论,这听起来像你有一些情况下,你希望一个转换器读取,但不写,其他人希望它写但没有阅读。通过将功能分成两个转换器,然后让每个转换器的CanConvert方法在适当的时候返回true或false,你已经解决了这个问题。这当然是一种可行的方法,并且似乎为你工作,这非常棒。不过,我想提供一个替代解决方案。

除了CanConvert方法,基地JsonConverter提供了两个可以覆盖的虚拟布尔属性:CanReadCanWrite。 (默认情况下均返回true。)这些属性直接控制序列化程序是否为特定转换器调用ReadJsonWriteJson。因此,例如,如果CanRead返回false,则不会调用ReadJson,但将使用默认读取行为,即使CanConvert返回true。这使您可以非常整洁地设置不对称转换器。例如,您可能会遇到这种情况,您想要将一种疯狂的JSON格式反序列化为更加健全的对象结构,但是当您再次序列化它时,您不想回到疯狂的JSON格式 - 您只需要默认序列化。在这种情况下,您将在转换器中覆盖CanWrite以始终返回false。然后,你可以将WriteJson的执行留空或让它出现NotImplementedException;它永远不会被调用。

你的情况听起来有点复杂,但你仍然应该能够操纵CanReadCanWrite属性来实现你想要的结果。以下是一个人为的例子,它展示了我们如何根据情境变量打开和关闭ReadJsonWriteJson方法。

public class Program 
{ 
    public static void Main(string[] args) 
    { 
     string json = @"{""keys"":[""foo"",""fizz""],""values"":[""bar"",""bang""]}"; 

     CustomConverter converter = new CustomConverter(); 
     JsonSerializerSettings settings = new JsonSerializerSettings(); 
     settings.Converters.Add(converter); 

     // Here we are reading a JSON object containing two arrays into a dictionary 
     // (custom read) and then writing out the dictionary JSON (standard write) 
     Console.WriteLine("--- Situation 1 (custom read, standard write) ---"); 
     converter.Behavior = ConverterBehavior.CustomReadStandardWrite; 
     json = DeserializeThenSerialize(json, settings); 

     // Here we are reading a simple JSON object into a dictionary (standard read) 
     // and then writing out a new JSON object containing arrays (custom write) 
     Console.WriteLine("--- Situation 2 (standard read, custom write) ---"); 
     converter.Behavior = ConverterBehavior.StandardReadCustomWrite; 
     json = DeserializeThenSerialize(json, settings); 
    } 

    private static string DeserializeThenSerialize(string json, JsonSerializerSettings settings) 
    { 
     Console.WriteLine("Deserializing..."); 
     Console.WriteLine(json); 
     var dict = JsonConvert.DeserializeObject<Dictionary<string, string>>(json, settings); 
     foreach (var kvp in dict) 
     { 
      Console.WriteLine(kvp.Key + ": " + kvp.Value); 
     } 

     Console.WriteLine("Serializing..."); 
     json = JsonConvert.SerializeObject(dict, settings); 
     Console.WriteLine(json); 
     Console.WriteLine(); 

     return json; 
    } 
} 

enum ConverterBehavior { CustomReadStandardWrite, StandardReadCustomWrite } 

class CustomConverter : JsonConverter 
{ 
    public ConverterBehavior Behavior { get; set; } 

    public override bool CanConvert(Type objectType) 
    { 
     return typeof(IDictionary<string, string>).IsAssignableFrom(objectType); 
    } 

    public override bool CanRead 
    { 
     get { return Behavior == ConverterBehavior.CustomReadStandardWrite; } 
    } 

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     Console.WriteLine("ReadJson was called"); 

     // Converts a JSON object containing a keys array and a values array 
     // into a Dictionary<string, string> 
     JObject jo = JObject.Load(reader); 
     return jo["keys"].Zip(jo["values"], (k, v) => new JProperty((string)k, v)) 
         .ToDictionary(jp => jp.Name, jp => (string)jp.Value); 
    } 

    public override bool CanWrite 
    { 
     get { return Behavior == ConverterBehavior.StandardReadCustomWrite; } 
    } 

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     Console.WriteLine("WriteJson was called"); 

     // Converts a dictionary to a JSON object containing 
     // a keys array and a values array from the dictionary 
     var dict = (Dictionary<string, string>)value; 
     JObject jo = new JObject(new JProperty("keys", new JArray(dict.Keys)), 
           new JProperty("values", new JArray(dict.Values))); 
     jo.WriteTo(writer); 
    } 
} 

输出:

--- Situation 1 (custom read, standard write) --- 
Deserializing... 
{"keys":["foo","fizz"],"values":["bar","bang"]} 
ReadJson was called 
foo: bar 
fizz: bang 
Serializing... 
{"foo":"bar","fizz":"bang"} 

--- Situation 2 (standard read, custom write) --- 
Deserializing... 
{"foo":"bar","fizz":"bang"} 
foo: bar 
fizz: bang 
Serializing... 
WriteJson was called 
{"keys":["foo","fizz"],"values":["bar","bang"]} 

小提琴:https://dotnetfiddle.net/BdtSoN

+0

令人敬畏的写作!事实上,我打算使用CanRead和CanWrite来分割我的读写逻辑。我很抱歉,我应该在我的评论中提到这一点。我想还有其他的方式来分割读写(就像使用两个不同的JsonSerializerSettings一样),但CanRead和CanWrite看起来是最直接的方式。 – SFun28

+0

不用担心。只是想确保你知道这些属性是可用的;我的印象是你可能错过了他们。 –