簡體   English   中英

JSON C#將具有1個項目的數組反序列化為對象

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

我有一個這樣的JSON結構,我無法更改此結構,因為它來自無法訪問的Web服務。

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

如您所見,每個屬性都有一個帶有一個確切參數的數組。 是否有一種簡單的方法可以將它們放入類似我的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; }
}

我已經找到了Json.net文檔 我可以用它來提取所有人。 但是我每個類有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,插入它應該不需要太多時間,我還需要對其進行分析。

處理大型JSON對象時,重要的是不要在最終反序列化之前將整個JSON流加載到中間表示形式中。 從而:

  1. 不要將JSON作為字符串下載。 性能提示

    為了最大程度地減少內存使用量和分配的對象數量,Json.NET支持直接對流進行序列化和反序列化。 在處理大小大於85kb的JSON文檔時,一次讀取或寫入JSON而不是將整個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. 不要僅僅為了反序列化"result"JArray整個JSON加載到JArray中。 而是使用JsonTextReader通過JSON進行流JsonTextReader直到找到名為"result"的屬性,然后反序列化其值,如JSON.NET中反序列化特定屬性所示

  3. 要自動將所有非集合值的對象屬性與單個條目數組之間進行映射,可以創建一個自定義IContractResolver ,將適當的自定義JsonConverter應用於適當類型的屬性。

綜合所有這些,您需要以下擴展方法和合同解析器:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM