【问题标题】:Deserialize JSON into C# dynamic object throw OutOfMemoryException exceptio将 JSON 反序列化为 C# 动态对象 throw OutOfMemoryException 异常
【发布时间】:2020-11-06 08:59:21
【问题描述】:

我正在使用 C# 动态功能来反序列化 Json。它非常适合小文件。但是,如果数据文件变大(我正在使用 500mb 文件进行测试),反序列化器将抛出如下所示的内存异常。

>Message: Exception of type 'System.OutOfMemoryException' was thrown.
>Source: mscorlib. StackTrace: at System.String.CtorCharArrayStartLength(Char[] value, Int32 startIndex, Int32 length)
   at Newtonsoft.Json.Utilities.StringReference.ToString()
   at Newtonsoft.Json.JsonTextReader.ParseReadNumber(ReadType readType, Char firstChar, Int32 initialPosition)
   at Newtonsoft.Json.JsonTextReader.ParseNumber(ReadType readType)
   at Newtonsoft.Json.JsonTextReader.ParseValue()
   at Newtonsoft.Json.JsonTextReader.Read()
   at Newtonsoft.Json.JsonWriter.WriteToken(JsonReader reader, Boolean writeChildren, Boolean writeDateConstructorAsDate, Boolean writeComments)
   at Newtonsoft.Json.Linq.JTokenWriter.WriteToken(JsonReader reader, Boolean writeChildren, Boolean writeDateConstructorAsDate, Boolean writeComments)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateJObject(JsonReader reader)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
   at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
   at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings)
   at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value, JsonSerializerSettings settings)
   at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value)
   at ShapeFile.Program.Main(String[] args) in C:\Users\HP\source\repos\ShapeFile\Program.cs:line 21

在任务管理器中,我可以看到内存消耗确实快速增长,直到它完全利用到我本地电脑的 16GB。 任何想法为什么会这样?

示例代码:

string jsonFile = @"D:\3D Map\Road\Road_3.geojson";
var myJsonResponse = File.ReadAllText(jsonFile);
dynamic myDeserializedClass = JsonConvert.DeserializeObject<dynamic>(myJsonResponse);

我认为这个问题可能是由于内存中有这么大的字符串,但无论如何我仍然需要动态读取这个 json 文件而不是创建一些类(实际上可以工作)以反序列化 JSON到 .NET 对象中。我不想创建类的原因是因为不同的json文件有不同的文件规格。

如果无法动态读取整个大型 json 文件,是否可以只读取示例 json 文件中的 Feature 和 Properties2 而无需为这两个文件创建类?下面还有示例类。

class Class1
{
    // Root myDeserializedClass = JsonConvert.DeserializeObject<Root>(myJsonResponse); 
    public class Properties
    {
        public string name { get; set; }
    }

    public class Crs
    {
        public string type { get; set; }
        public Properties properties { get; set; }
    }

    public class Properties2
    {
        public int ID_0 { get; set; }
        public string ISO { get; set; }
        public string NAME_0 { get; set; }
        public int ID_1 { get; set; }
        public string NAME_1 { get; set; }
        public int ID_2 { get; set; }
        public string NAME_2 { get; set; }
        public string TYPE_2 { get; set; }
        public string ENGTYPE_2 { get; set; }
        public object NL_NAME_2 { get; set; }
        public string VARNAME_2 { get; set; }
    }

    public class Geometry
    {
        public string type { get; set; }
        public List<dynamic> coordinates { get; set; }
        
    }

    public class Feature
    {
        public string type { get; set; }
        public int id { get; set; }
        public Properties2 properties { get; set; }
        public Geometry geometry { get; set; }
    }

    public class Root
    {
        public string type { get; set; }
        public string name { get; set; }
        public Crs crs { get; set; }
        public List<Feature> features { get; set; }
    }
}

下面的示例 Json 文件。

{
    "type": "FeatureCollection",
    "name": "MYS_adm2",
    "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },
    "features": [
    { "type": "Feature", "id": 0, "properties": { "ID_0": 136 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 102.911849975585938, 1.763612031936702 ], [ 102.911430358886832, 1.763888001442069 ] ] ] } },
    { "type": "Feature", "id": 1, "properties": { "ID_0": 136 }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ 103.556556701660156, 1.455448031425533 ], [ 103.555900573730582, 1.455950021743831 ] ] ] ] } },

【问题讨论】:

  • 仅仅读取文本文件会消耗多少内存?也许不是将字符串加载到内存中,而是使用读取器对象?
  • 你的意思是手动打开json文本文件?消耗了将近2GB。抱歉,您所说的使用阅读器对象是什么意思?介意分享一下吗?
  • 打开文件进行读取,并将其传递给反序列化器而不是整个字符串,因此您可以尝试类似:using (var reader = File.OpenText(@"path")) { Root root = (Root)new JsonSerializer().Deserialize(reader, typeof(Root)); } 不幸的是,JsonConvert 似乎没有静态反序列化方法接受阅读器,但改用 JsonSerializer 对象方法。
  • 谢谢,我明白了。然而回到我的目的是动态读取 json 文件,所以这里不是使用 Root (实际上就像我说的那样工作),是否可以使用你建议的 dynamic 方法?
  • using (var reader = File.OpenText(filePath)) { dynamic root = (dynamic)new JsonSerializer().Deserialize(new JsonTextReader(reader)); } 也尝试过同样的异常。

标签: c# json geojson json-deserialization


【解决方案1】:

我假设您想从包含大量节点的features 字段中读取所有数据。您可以考虑使用Cinchoo ETL - 一个开源库来读取如此大的文件。

using (var r = new ChoJSONReader("*** YOUR JSON FILE PATH ***")
    .WithJSONPath("$.features")
    )
{
    foreach (var rec in r)
        Console.WriteLine(rec.Dump());
}

【讨论】:

    猜你喜欢
    • 2011-03-09
    • 1970-01-01
    • 2019-07-21
    • 1970-01-01
    • 1970-01-01
    • 2022-01-21
    • 1970-01-01
    • 2015-08-05
    相关资源
    最近更新 更多