JSON C#反序列化数组有1项对象

时间:2016-04-22 14:54:18

标签: c# arrays json serialization json.net

我有这样的JSON结构,我无法更改此结构,因为它来自我无法访问的Web服务。

[
  {
    "query": "BusinessFunction",
    "result": [
      {
        "id": [
          "10247"
        ],
        "lastModificationUser": [
          "maxmustermann"
        ],
        "description": [],
        "name": [
          "Engineering Data Mgmt"
        ],
       ...
      },
      {
        "id": [
          "10455"
        ],
        ...
      }
  },
  ...
]

正如您所看到的,每个Attribut都有一个带有一个确切参数的数组。是否有一种简单的方法可以将它们放入像我的类BusinessFunctionData这样的构造中,而无需手动提取每个参数?

class BusinessFunctionData
{
    [JsonProperty(PropertyName = "id")]
    public string id { get; set; }
    [JsonProperty(PropertyName = "lastModificationUser")]
    public string lastModificationUser { get; set; }
    [JsonProperty(PropertyName = "description")]
    public string description { get; set; }
}

我已找到Json.net Documentation。我可以用它来提取每个人。但是我每班有200多个参数,所以我不确定性能和可用性。

也许某人有一个更容易,更快的想法。

我试图找到一个解决方案,我可以使用类似于此的东西:

    public IList<BusinessFunctionData> deseralize(string jsonstring)
    {
        var data = JArray.Parse(jsonstring);
        IList<BusinessFunctionData> outputlist = new List<BusinessFunctionData>();
        var JsonProgramData = data[0]["result"].Children().ToList();
        foreach (var prog in JsonProgramData)
        {

            BusinessFunctionData programm = JsonConvert.DeserializeObject<BusinessFunctionData>(prog.ToString());
            outputlist.Add(programm);
        }
        return outputlist;
    }  

我希望有人能回答我关于表现的问题。当我下载json文件时,它超过100mb,并且它不应该花费太多时间来获取它,我需要另外分析它。

1 个答案:

答案 0 :(得分:0)

在处理大型JSON对象时,重要的是不要在最终反序列化之前将整个JSON流加载到中间表示中。因此:

  1. 不要将JSON下载为字符串。来自Performance Tips

      

    为了最大限度地减少内存使用量和分配的对象数量,Json.NET支持直接序列化和反序列化流。一次读取或写入JSON,而不是将整个JSON字符串加载到内存中,在处理大小超过85kb的JSON文档时尤为重要,以避免JSON字符串在大对象堆中结束。

    相反,Newtonsoft建议直接从响应流反序列化,例如:

    HttpClient client = new HttpClient();
    
    using (Stream s = client.GetStreamAsync("http://www.test.com/large.json").Result)
    using (StreamReader sr = new StreamReader(s))
    using (JsonReader reader = new JsonTextReader(sr))
    {
        JsonSerializer serializer = new JsonSerializer();
    
        // read the json from a stream
        // json size doesn't matter because only a small piece is read at a time from the HTTP request
        RootObject root = serializer.Deserialize<RootObject>(reader);
    }
    
  2. 不要将整个JSON加载到JArray中,只是为了反序列化"result"值。而是使用JsonTextReader流式传输JSON,直到找到名为"result"的属性,然后反序列化其值,如JSON.NET deserialize a specific property所示。

  3. 要自动将所有非集合值对象属性映射到单条目数组,您可以创建custom IContractResolver,将适当的custom JsonConverter应用于相应类型的属性。

  4. 将所有这些放在一起,您需要以下扩展方法和合同解析器:

    public static class JsonExtensions
    {
        public static IEnumerable<T> DeserializeNamedProperties<T>(Stream stream, string propertyName, JsonSerializerSettings settings = null, int? depth = null)
        {
            using (var textReader = new StreamReader(stream))
                foreach (var value in DeserializeNamedProperties<T>(textReader, propertyName, settings, depth))
                    yield return value;
        }
    
        public static IEnumerable<T> DeserializeNamedProperties<T>(TextReader textReader, string propertyName, JsonSerializerSettings settings = null, int? depth = null)
        {
            var serializer = JsonSerializer.CreateDefault(settings);
            using (var jsonReader = new JsonTextReader(textReader))
            {
                while (jsonReader.Read())
                {
                    if (jsonReader.TokenType == JsonToken.PropertyName
                        && (string)jsonReader.Value == propertyName
                        && depth == null || depth == jsonReader.Depth)
                    {
                        jsonReader.Read();
    
                        yield return serializer.Deserialize<T>(jsonReader);
                    }
                }
            }
        }
    }
    
    public class ArrayToSingleContractResolver : DefaultContractResolver
    {
        // As of 7.0.1, Json.NET suggests using a static instance for "stateless" contract resolvers, for performance reasons.
        // http://www.newtonsoft.com/json/help/html/ContractResolver.htm
        // http://www.newtonsoft.com/json/help/html/M_Newtonsoft_Json_Serialization_DefaultContractResolver__ctor_1.htm
        // "Use the parameterless constructor and cache instances of the contract resolver within your application for optimal performance."
        static ArrayToSingleContractResolver instance;
    
        static ArrayToSingleContractResolver() { instance = new ArrayToSingleContractResolver(); }
    
        public static ArrayToSingleContractResolver Instance { get { return instance; } }
    
        readonly SimplePropertyArrayToSingleConverter simpleConverter = new SimplePropertyArrayToSingleConverter();
    
        protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
        {
            var jsonProperty = base.CreateProperty(member, memberSerialization);
            if (jsonProperty.Converter == null && jsonProperty.MemberConverter == null)
            {
                if (jsonProperty.PropertyType.IsPrimitive 
                    || jsonProperty.PropertyType == typeof(string))
                {
                    jsonProperty.Converter = jsonProperty.MemberConverter = simpleConverter;
                }
                else if (jsonProperty.PropertyType != typeof(object)
                    && !typeof(IEnumerable).IsAssignableFrom(jsonProperty.PropertyType)
                    && !typeof(JToken).IsAssignableFrom(jsonProperty.PropertyType))
                {
                    jsonProperty.Converter = jsonProperty.MemberConverter = new ObjectPropertyArrayToSingleConverter(this, jsonProperty.PropertyType);
                }
            }
    
            return jsonProperty;
        }
    }
    
    public static class JsonContractExtensions
    {
        public static bool? IsArrayContract(this JsonContract contract)
        {
            if (contract == null)
                throw new ArgumentNullException();
            if (contract is JsonArrayContract)
                return true;
            else if (contract is JsonLinqContract)
                return null; // Could be an object or an array.
            else
                return false;
        }
    }
    
    class SimplePropertyArrayToSingleConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            throw new NotImplementedException();
        }
    
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            while (reader.TokenType == JsonToken.Comment)
                reader.Read();
            if (reader.TokenType == JsonToken.Null)
                return null;
            var contract = serializer.ContractResolver.ResolveContract(objectType);
            bool hasValue = false;
            if (reader.TokenType == JsonToken.StartArray)
            {
                while (reader.Read())
                {
                    switch (reader.TokenType)
                    {
                        case JsonToken.Comment:
                            break;
                        case JsonToken.EndArray:
                            return UndefaultValue(objectType, existingValue, contract);
                        default:
                            if (hasValue)
                                throw new JsonSerializationException("Too many values at path: " + reader.Path);
                            existingValue = ReadItem(reader, objectType, existingValue, serializer, contract);
                            hasValue = true;
                            break;
                    }
                }
                // Should not come here.
                throw new JsonSerializationException("Unclosed array at path: " + reader.Path);
            }
            else
            {
                existingValue = ReadItem(reader, objectType, existingValue, serializer, contract);
                return UndefaultValue(objectType, existingValue, contract);
            }
        }
    
        private static object UndefaultValue(Type objectType, object existingValue, JsonContract contract)
        {
            if (existingValue == null && objectType.IsValueType && Nullable.GetUnderlyingType(objectType) == null)
                existingValue = contract.DefaultCreator();
            return existingValue;
        }
    
        private static object ReadItem(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer, JsonContract contract)
        {
            if (contract is JsonPrimitiveContract || existingValue == null)
            {
                existingValue = serializer.Deserialize(reader, objectType);
            }
            else
            {
                serializer.Populate(reader, existingValue);
            }
            return existingValue;
        }
    
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            writer.WriteStartArray();
            if (value != null)
                serializer.Serialize(writer, value);
            writer.WriteEndArray();
        }
    }
    
    class ObjectPropertyArrayToSingleConverter : SimplePropertyArrayToSingleConverter
    {
        readonly Type propertyType;
        readonly IContractResolver resolver;
        int canConvert = -1;
    
        public ObjectPropertyArrayToSingleConverter(IContractResolver resolver, Type propertyType)
            : base()
        {
            if (propertyType == null || resolver == null)
                throw new ArgumentNullException();
            this.propertyType = propertyType;
            this.resolver = resolver;
        }
    
        int GetIsEnabled()
        {
            var contract = resolver.ResolveContract(propertyType);
            return contract.IsArrayContract() == false ? 1 : 0;
        }
    
        bool IsEnabled
        {
            get
            {
                // We need to do this in a lazy fashion since recursive calls to resolve contracts while creating a contract are problematic.
                if (canConvert == -1)
                    Interlocked.Exchange(ref canConvert, GetIsEnabled());
                return canConvert == 1;
            }
        }
    
        public override bool CanRead { get { return IsEnabled; } }
    
        public override bool CanWrite { get { return IsEnabled; } }
    }
    

    然后使用它:

    string url = @"..."; // Replace with your actual URL.
    
    IList<BusinessFunctionData> outputlist;
    
    WebRequest request = WebRequest.Create(url);
    using (var response = request.GetResponse())
    using (var responseStream = response.GetResponseStream())
    {
        var settings = new JsonSerializerSettings { ContractResolver = ArrayToSingleContractResolver.Instance, NullValueHandling = NullValueHandling.Ignore };
        outputlist = JsonExtensions.DeserializeNamedProperties<List<BusinessFunctionData>>(responseStream, "result", settings).FirstOrDefault();
    }