JSON C#将具有1项的数组反序列化为对象 [英] JSON C# deserialize Array With 1 item to Object

查看:55
本文介绍了JSON C#将具有1项的数组反序列化为对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a JSON structure like this, I cannot Change this structure, because it comes from a webservice which i cannot access.

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

如您所见,每个属性都有一个带有一个确切参数的数组.是否有一种简单的方法可以将它们放入类似我的类BusinessFunctionData的结构中,而无需手动提取每个参数?

As you can see, every Attribut got an array with one exactly parameter. Is there a simple way to get them into a construct like my class BusinessFunctionData without extract every parameter manually?

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文档.我可以用它来提取所有人.但是我每个类有200多个参数,所以我不确定性能和可用性.

I already found Json.net Documentation. I could use this, to extract everyone. But I have over 200 Parameters per class, so i'm not sure about performance and usabillity.

也许有人有一个想法,那就是更容易,更快.

Maybe someone got an idea thats easier and faster.

我试图找到一种解决方案,在其中我可以使用比这更相似的东西:

I try to get a solution where i can use something simular than this:

    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,插入它应该不需要太多时间,我还需要对其进行分析.

I'm hopeing someone can answer my question about performance. When i download the json file, it's over 100mb big, and it should'nt take too much time to get it in, i need to analyze it in addition.

推荐答案

在处理大型JSON对象时,在最终反序列化之前,请勿将整个JSON流加载到中间表示形式中,这一点很重要.因此:

When dealing with large JSON objects, it is important not to load the entire JSON stream into an intermediate representation before final deserialization. Thus:

  1. 请勿将JSON作为字符串下载.来自性能提示:

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

To minimize memory usage and the number of objects allocated, Json.NET supports serializing and deserializing directly to a stream. Reading or writing JSON a piece at a time, instead of having the entire JSON string loaded into memory, is especially important when working with JSON documents greater than 85kb in size to avoid the JSON string ending up in the large object heap.

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

Instead, Newtonsoft recommends to deserialize directly from the response stream, e.g.:

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);
}

  • 请勿仅为了反序列化"result"值而将整个JSON加载到JArray中.而是使用 JsonTextReader 遍历JSON,直到找到一个属性名为"result",然后反序列化其值,如 JSON.NET反序列化特定属性所示.

  • Do not load your entire JSON into a JArray simply in order to deserialize the "result" value. Instead stream through the JSON with a JsonTextReader until you find a property named "result" and then deserialize its value, as is shown in JSON.NET deserialize a specific property.

    要自动将所有非集合值的对象属性映射到单入口数组以及将其映射到单入口数组,可以创建

    To automatically map all non-collection-valued object properties from and to single-entry arrays, you can create a custom IContractResolver that applies an appropriate custom JsonConverter to properties of the appropriate type.

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

    Putting all this together, you need the following extension methods and contract resolver:

    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();
    }
    

    这篇关于JSON C#将具有1项的数组反序列化为对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆