【问题标题】:How to discard JSON elements with specific values while deserializing如何在反序列化时丢弃具有特定值的 JSON 元素
【发布时间】:2020-10-06 06:13:33
【问题描述】:

这是一个有趣的情况。我的应用程序从 MongoDB 保存和加载 JSON 数据,这在 95% 的时间都可以正常工作。这种情况下的 JSON 数据如下:

{
    "isDemo": true,
    "CustomerReference": "nabTest",
    "Fee": null,
    "OrderId": "48/XYZ3",   
    "Asynchronous": false,
}

剩下 5% 的时间,遗留应用程序会加载一些 XML 数据,将其转换为 JSON 格式并将其插入到同一个 MongoDB 集合中。这种情况下的JSON数据是这样的:

{
   "@xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance",
   "@xmlns:xsd":"http://www.w3.org/2001/XMLSchema",
   "@xmlns":"http://MyDomain.com.au/SomeService.Contract/2007/06/18",
   "isDemo":{
      "@xmlns":"http://MyDomain.com.au/ABC.Services",
      "#text":"true"
   },
   "Asynchronous":{
      "@xmlns":"http://MyDomain.com.au/ABC.Services"
      "#text":"false"
   },
   "Fee":{
      "@xsi:nil":"true",
      "@xmlns":"http://MyDomain.com.au/ABC.Services"
   },
   "CustomerReference":{
      "@xmlns":"http://MyDomain.com.au/ABC.Services"      
   },
   "OrderId":"48/XYZ3"
}

在这种情况下,Newtonsoft 反序列化器会崩溃并出现以下异常:

Newtonsoft.Json.JsonReaderException: '遇到意外字符 解析值时:{。路径“客户参考”,第 1 行,位置 919.'

最少的代码如下:

// 'result' is of type object, loaded from MongoDB
var resultStr = JsonConvert.SerializeObject(result);
var obj = JsonConvert.DeserializeObject<T>(resultStr, jsonSerializerSettings);

什么是处理这种情况的优雅方法,只需最少的代码更改。

【问题讨论】:

  • 处理这个最优雅的方法是确保你没有将所有的 xml 命名空间放入 json,如果你不能这样做,你可能需要编写一个转换器并过滤掉所有的垃圾
  • 没有一个 Json 告诉我们费用是什么类型。您介意给出第一个 Json 的对象表示吗?

标签: c# json.net


【解决方案1】:

普通 Json 的对象表示应该是这样的:

using J = Newtonsoft.Json.JsonPropertyAttribute; 
public partial class Data
{
       [J("isDemo")]            public bool? IsDemo { get; set; }             
       [J("Asynchronous")]      public bool? IsAsynchronous { get; set; }    
         
       [J("OrderId")]           public string OrderId { get; set; }          
       [J("CustomerReference")] public string CustomerReference { get; set; }
       [J("Fee")]               public double? Fee { get; set; }              
}

虽然有一个自定义的JsonConverter 来处理整个 Xml 表示会很好。我发现很难编写和维护。
你应该分而治之:

  • 生成一个表示 Xml-ish Json 的类。
  • 删除不必要的属性,在需要的地方保留 Xml“nil”
  • 创建简单的自定义转换器,将字符串表示映射到正确的类型。 例如:“真”=>布尔,“123”=>整数,
public partial class XmlRepresentation
{                        
  [J("isDemo")]            public BoolWrapper IsDemo { get; set; }                
  [J("Asynchronous")]      public BoolWrapper Asynchronous { get; set; }          
  [J("Fee")]               public DoubleWrapper Fee { get; set; }                            
  [J("CustomerReference")] public StringWrapper CustomerReference { get; set; }
  [J("OrderId")]           public string OrderId { get; set; }                     
}

public partial class NullableXmlValue
{   
  [J("@xsi:nil")][JsonConverter(typeof(BoolParseStringConverter))] 
  public bool IsNull { get; set; }
}
public partial class BoolWrapper :NullableXmlValue
{
  [J("#text")][JsonConverter(typeof(BoolParseStringConverter))] 
  public bool Value { get; set; }
}

public partial class StringWrapper :NullableXmlValue
{
  [J("#text")] public string Value { get; set; }
}

public partial class DoubleWrapper :NullableXmlValue
{
  [J("#text")][JsonConverter(typeof(DoubleParseStringConverter))] 
  public double Value { get; set; }
}

使用一些简单的 String 类型 {Bool, Double} 解析器:

internal class BoolParseStringConverter : JsonConverter
{
   public override bool CanConvert(Type t) => t == typeof(bool) || t == typeof(bool?);

   public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
   {
      if (reader.TokenType == JsonToken.Null) return null;
      var value = serializer.Deserialize<string>(reader);
      if (Boolean.TryParse(value, out bool b))
      {
         return b;
      }
      throw new Exception("Cannot unmarshal type bool");
   }

   public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
   {
      if (untypedValue == null)
      {
         serializer.Serialize(writer, null);
         return;
      }
      var value = (bool)untypedValue;
      var boolString = value ? "true" : "false";
      serializer.Serialize(writer, boolString);
      return;
   }

   public static readonly BoolParseStringConverter Singleton = new BoolParseStringConverter();
}

internal class DoubleParseStringConverter : JsonConverter
{
   public override bool CanConvert(Type t) => t == typeof(double) || t == typeof(double?);

   public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
   {
      if (reader.TokenType == JsonToken.Null) return null;
      var value = serializer.Deserialize<string>(reader);
      if (Double.TryParse(value, out double l))
      {
         return l;
      }
      throw new Exception("Cannot unmarshal type long");
   }

   public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
   {
      if (untypedValue == null)
      {
         serializer.Serialize(writer, null);
         return;
      }
      var value = (double)untypedValue;
      serializer.Serialize(writer, value.ToString());
      return;
   }

   public static readonly DoubleParseStringConverter Singleton = new DoubleParseStringConverter();
}

这样就可以直接反序列化为XmlRepresentation,使用简单的投影逻辑来获取Data。

public partial class XmlRepresentation
{  
   public Data ToDataType(){
      var result = new Data();
      if( !this.IsDemo?.IsNull ?? false)
         result.IsDemo = this.IsDemo.Value;

       // etc..

       return result;
   }
}

为了知道它是普通的 Json 还是 Xml-ish 的,我将简单地使用 try catch。 这样我就不必编写复杂的 JsonConverter 或代表 Normal 和 Xml-ish 的类。

【讨论】:

  • @CodeCaster,在写这篇文章之前,我已经看到并测试了你提出的解决方案。阅读文档应该可以正常工作。但我天真的尝试失败了dotnetfiddle.net/jodh7c。所以我想如果出于某种原因它根本不是有效的 Xml 怎么办。并想出了这个。但是我没有玩到 ``DeserializeXmlNode 并且无法判断 Xml 是坏的还是损坏的。我知道这是很多代码,可以看作是复杂的。例如,可以删除所有转换器,我们将所有内容映射到字符串,仅在 ToDataType() 中进行转换
  • @CodeCaster,我认为对于任何将真正的 Xml 作为 Json 的人来说,这仍然是一个很好的答案。
【解决方案2】:

请在模型中这样使用:

public class User
{
    // always require a string value
    [JsonProperty("name", Required = Required.Always)]
    public string Name { get; set; }

    // don't require any value
    [JsonProperty("role", NullValueHandling = NullValueHandling.Ignore)]
    public string Role { get; set; }

    // property is ignored
    [JsonIgnore]
    public string Password { get; set; }
}

使用 Json.NET 序列化属性生成的结果:

{
"type": "object",
    "properties": {
        "name": {
        "type": "string"
        },
        "role": {
            "type": [
                "string",
                "null"
            ]
        }
    },
    "required": [
        "name"
    ]
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-21
    • 1970-01-01
    相关资源
    最近更新 更多