【问题标题】:JsonTextReader gets mixed up after networking error网络错误后 JsonTextReader 搞混了
【发布时间】:2020-12-30 11:11:53
【问题描述】:

我正在尝试制作一个程序,该程序使用数据来寻找大型 3D 数学网络的解决方案。在 JSON 格式中,有一个节点数组和关于它们在 3D 空间中的位置以及它们具有哪些属性等的信息,使用行分隔格式:

{"ID": 1, "Type": "Node", "OtherInfo":["info1", "info2"], "x": 0, "y": 0, "z": 0}
{"ID": 2, "Type": "OtherNode", "OtherInfo":["otherinfo1", "otherinfo2"], "x": 1, "y": 0, "z": 0}
...

我的班级:

class Node
{
    ulong ID { get; set; }
    nodeType Type { get; set; }
    string[] otherInfo { get; set; }
    decimal X { get; set; }
    decimal Y { get; set; }
    decimal Z { get; set; }
}

我的获取方法:

public static IEnumerable<Node> ReadStream(Stream stream)
{
    var serializer = new JsonSerializer();

    using var reader = new StreamReader(stream);
    using var jsonReader = new JsonTextReader(reader) { SupportMultipleContent = true };

    Stopwatch foo = new Stopwatch();

    bool ll = jsonReader.Read();
    while (ll)
    {
        Node node = null;
        try
        {
            node = serializer.Deserialize<Node>(jsonReader);
            ll = jsonReader.Read();
        }
        catch (Exception e)
        {
            Console.WriteLine($"Exception {e}");
            continue;
        }
        yield return node;
    }
}

我将变量 var stream = await HttpClient.GetStreamAsync("https://example.com/nodes.jsonl"); 传递给函数,它大多数运行良好,每 1/50000 个节点失败一次。问题是一旦失败,在 65,000 个节点中,它会开始读取 "info1""otherinfo1" 作为需要对未能反序列化的对象进行反序列化的节点。

Exception Newtonsoft.Json.JsonSerializationException: Error converting value "info1" to type 'Starbreaker.Node'. Path 'type', line 61876, position 295. ---> System.ArgumentException: Could not cast or convert from System.String to Starbreaker.Node.
   at Newtonsoft.Json.Utilities.ConvertUtils.EnsureTypeAssignable(Object value, Type initialType, Type targetType)
   at Newtonsoft.Json.Utilities.ConvertUtils.ConvertOrCast(Object initialValue, CultureInfo culture, Type targetType)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.EnsureType(JsonReader reader, Object value, CultureInfo culture, JsonContract contract, Type targetType)
   --- End of inner exception stack trace ---
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.EnsureType(JsonReader reader, Object value, CultureInfo culture, JsonContract contract, Type targetType)
   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.JsonSerializer.Deserialize[T](JsonReader reader)
   at Starbreaker.settings.<ReadStream>d__1.MoveNext() in C:\repos\Starbreaker\Starbreaker\Form1.cs:line 361

这是我得到的网络错误之一:

Exception System.IO.IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host. ---> System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host
   at System.Net.Sockets.Socket.Receive(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags)
   at System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 size)
   --- End of inner exception stack trace ---
   at System.Net.ConnectStream.Read(Byte[] buffer, Int32 offset, Int32 size)
   at System.Net.Http.HttpClientHandler.WebExceptionWrapperStream.Read(Byte[] buffer, Int32 offset, Int32 count)
   at System.Net.Http.DelegatingStream.Read(Byte[] buffer, Int32 offset, Int32 count)
   at System.IO.StreamReader.ReadBuffer(Char[] userBuffer, Int32 userOffset, Int32 desiredChars, Boolean& readToUserBuffer)
   at System.IO.StreamReader.Read(Char[] buffer, Int32 index, Int32 count)
   at Newtonsoft.Json.JsonTextReader.ReadData(Boolean append, Int32 charsRequired)
   at Newtonsoft.Json.JsonTextReader.ReadStringIntoBuffer(Char quote)
   at Newtonsoft.Json.JsonTextReader.ParseProperty()
   at Newtonsoft.Json.JsonTextReader.ParseObject()
   at Newtonsoft.Json.JsonTextReader.Read()
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateObject(Object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, String id)
   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.JsonSerializer.Deserialize[T](JsonReader reader)
   at Starbreaker.settings.<ReadStream>d__1.MoveNext() in C:\repos\Starbreaker\Starbreaker\Form1.cs:line 361

无论是否有错误,读取功能似乎都会移动位置。发生网络错误时,是否有办法将其移回行首,或者是否有其他方法可以解决此问题?

【问题讨论】:

    标签: c# json .net sockets


    【解决方案1】:

    我不知道服务器发生了什么,但我将专注于如何从解析独立 JSON 块流中恢复。问题是您可能收到Newtonsoft.Json 无法恢复的格式错误的数据。一旦发生这种情况,你就不走运了。在这种情况下,即使您的例程中的adding error handling 也不起作用。所以,你应该考虑的是这样的:

    private static IEnumerable<Node> ReadStream(Stream stream)
    {
        using var reader = new StreamReader(stream);
        while (!reader.EndOfStream)
        {
            var line = reader.ReadLine();
            Node node;
            try
            {
                node = JsonConvert.DeserializeObject<Node>(line);
            }
            catch (JsonReaderException ex)
            {
                Console.WriteLine($"Failed to parse line: {line}\nException: {ex}");
                continue;
            }
            yield return node;
        }
    }
    

    如果存在格式错误的数据,则无关紧要。您将读取行的责任委托给真正擅长并且在正常情况下永远不会出错的事情(正常情况下是良好的流具有格式正确或格式错误的数据)

    我尝试使用您的数据并故意输入错误数据。这个方法只是直接跳过了坏数据并继续到下一行。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-03-05
      • 1970-01-01
      • 1970-01-01
      • 2017-02-09
      • 2010-12-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多