简体   繁体   English

JSON C#将具有1个项目的数组反序列化为对象

[英]JSON C# deserialize Array With 1 item to Object

I have a JSON structure like this, I cannot Change this structure, because it comes from a webservice which i cannot access. 我有一个这样的JSON结构,我无法更改此结构,因为它来自无法访问的Web服务。

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

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? 是否有一种简单的方法可以将它们放入类似我的BusinessBusinessData类的结构中,而无需手动提取每个参数?

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

I already found Json.net Documentation . 我已经找到了Json.net文档 I could use this, to extract everyone. 我可以用它来提取所有人。 But I have over 200 Parameters per class, so i'm not sure about performance and usabillity. 但是我每个类有200多个参数,所以我不确定性能和可用性。

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

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文件时,它的大小超过100mb,插入它应该不需要太多时间,我还需要对其进行分析。

When dealing with large JSON objects, it is important not to load the entire JSON stream into an intermediate representation before final deserialization. 处理大型JSON对象时,重要的是不要在最终反序列化之前将整个JSON流加载到中间表示形式中。 Thus: 从而:

  1. Do not download the JSON as a string. 不要将JSON作为字符串下载。 From Performance Tips : 性能提示

    To minimize memory usage and the number of objects allocated, Json.NET supports serializing and deserializing directly to a stream. 为了最大程度地减少内存使用量和分配的对象数量,Json.NET支持直接对流进行序列化和反序列化。 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. 在处理大小大于85kb的JSON文档时,一次读取或写入JSON而不是将整个JSON字符串加载到内存中尤其重要,这样可以避免JSON字符串出现在大对象堆中。

    Instead, Newtonsoft recommends to deserialize directly from the response stream, eg: 相反,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. Do not load your entire JSON into a JArray simply in order to deserialize the "result" value. 不要仅仅为了反序列化"result"JArray整个JSON加载到JArray中。 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 . 而是使用JsonTextReader通过JSON进行流JsonTextReader直到找到名为"result"的属性,然后反序列化其值,如JSON.NET中反序列化特定属性所示

  3. 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. 要自动将所有非集合值的对象属性与单个条目数组之间进行映射,可以创建一个自定义IContractResolver ,将适当的自定义JsonConverter应用于适当类型的属性。

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

Then use it like: 然后像这样使用它:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM