V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
GeekHub
elsagong
V2EX  ›  程序员

(附完整代码)求助懂 C#的大神, Excel 中显示 DataSet 数据集遇到 System.NullReferenceException 的报错是 NewtonJson Jarray 造成的吗

  •  
  •   elsagong · 278 天前 · 1513 次点击
    这是一个创建于 278 天前的主题,其中的信息可能已经有所发展或是发生改变。

    如题,我想在 Excel 中显示如下 JSON 数据集( Python 写的 API 接口)

    {
        "msg": "success",
        "state": 10000,
        "data": {
            "data": [
                {
                    "index": 0,
                    "test_1": 110,
                    "test_2": "000001",
                    "test_3": "CN",
                    "test_4": "Bank",
                    "test_5": 893,
                    "test_6": 229
                }
            ],
            "schema": {
                "fields": [
                    {
                        "type": "integer",
                        "name": "index"
                    },
                    {
                        "type": "string",
                        "name": "test_1"
                    },
                    {
                        "type": "string",
                        "name": "test_2"
                    },
                    {
                        "type": "number",
                        "name": "test_3"
                    },
                    {
                        "type": "number",
                        "name": "test_4"
                    },
                    {
                        "type": "number",
                        "name": "test_5"
                    },
                    {
                        "type": "string",
                        "name": "test_6"
                    }
                ],
                "pandas_version": "0.20.0",
                "primaryKey": [
                    "index"
                ]
            }
        }
    }
    

    下面是我 C#的数据集代码 TestDataset.cs:

    using System;
    using System.IO;
    using System.Net;
    using Newtonsoft.Json.Linq;
    using ExcelDna.Integration;
    using Excel = Microsoft.Office.Interop.Excel;
    namespace Test_Atune
    {
        public class Request
        {
            Excel.Application ExcelApp = (Excel.Application)ExcelDnaUtil.Application;
            public object GetDatasetFromUrl(string root, string path, string headval, Excel.Range excelAddress)
            {
                string historicalData= "{}";
                var webConnection = new WebClient();
                webConnection.Headers.Add("Host", headval);
                try
                {
                    historicalData = webConnection.DownloadString(root+path);
                }
                catch (WebException ex)
                {
                    string error_str = ex.Message;
                    if (ex.Status.ToString() == "ConnectFailure" || ex.Status.ToString() == "Timeout")
                    {
                        root = Constant.setURL_ROOT_COMMON(root);
                        try
                        {
                            historicalData= webConnection.DownloadString(root + path);
                        }
                        catch (WebException ex2)
                        {
                            return "";
                        }
                    }
                }
                finally
                {
                    webConnection.Dispose();
                }
    
                JObject jsonFeed = JObject.Parse(historicalData);
                Excel.Range range = excelAddress;
                JObject B = new JObject();
                JArray data = (JArray)jsonFeed["data"]["data"];
                JArray columns = (JArray)jsonFeed["data"]["schema"]["fields"];
                int rowNum = data.Count;
                int colNum = columns.Count;
                Excel.Range range_head = ExcelApp.Cells[range.Row + 1, range.Column];
                range_head = range_head.get_Resize(1, colNum);
                Excel.Range range_data = ExcelApp.Cells[range.Row + 2, range.Column];
                range_data = range_data.get_Resize(rowNum, colNum);
                // write header
                object[,] headerData = new object[1, colNum];
                for (int iCol = 0; iCol < colNum; iCol++)
                {
                    headerData[0, iCol] = columns[iCol]["name"];
                }
                range_head.Value2 = headerData;
    
                // write data
                object[,] cellData = new object[rowNum, colNum];
                int iRow = 0;
                foreach (JObject jo in data)
                {
                    var a = jo["test_code"];
                    for (int iCol = 0; iCol < colNum; iCol++)
                    {
                        if (columns[iCol]["test_1"].ToString() == "string")
                        {
                            cellData[iRow, iCol] = "'" + jo[columns[iCol]["name"].ToString()];
                        }
                        else
                        {
                            cellData[iRow, iCol] = jo[columns[iCol]["name"].ToString()];
                        }
                    }
                    iRow += 1;
                }
                range_data.Value2 = cellData;
                return "Total" + rowNum.ToString() + "cells";
            }
        }
    
    }
    

    请求 Python 接口的 C#代码 request.cs

    using ExcelDna.Integration;
    using Excel = Microsoft.Office.Interop.Excel;
    namespace Test_Atune
    {
        public class Dataset
        {
            public static string baseurl = Constant.URL_ROOT_COMMON;
            public static string headval = Constant.HEADVAL_COMMON;
            public static Request request = new Request();
    
            [ExcelFunction(Category = "test", IsMacroType = true, Description = "test dataset")]
            public static object TEST_DATASET(
                        [ExcelArgument(Description = "test_code")] string test_1,
                        [ExcelArgument(Description = "YYYYMMDD")] string test_2,
                        [ExcelArgument(Description = "YYYYMMDD")] string test_3
                        )
            {
                string parstr = @"/test_dataset/?" +
                    @"test_1=" + test_1 +
                    @"&test_2=" + test_2 +
                    @"&test_3=" + test_3;
                ExcelReference caller = (ExcelReference)XlCall.Excel(XlCall.xlfCaller);
                Excel.Range rg = caller.ToPiaRange();
                return ExcelAsyncUtil.Run("TEST_DATASET",
                    new object[] { parstr },
                    () => request.GetDatasetFromUrl(Constant.URL_ROOT_COMMON, parstr, headval, rg));
            }
        }
    
    }
    

    但在这一行 JArray columns = (JArray)jsonFeed["data"]["schema"]["fields"];

    我遇到了如下报错:

    System.NullReferenceException HResult=0x80004003  message=Object reference not set to an instance of an object.
    

    Debug 后得到如下结果,是否 NewtonJson Jarray 需要判断下是否为空值?数据集不太会判别,对 C#一窍不通,求大神指点一二,非常感谢 Name: historicalData, Value:"{}"; Name: jsonFeed,Vaule:Null; Name:B,Vaule:{{}}, Name:data, Value:"null"

    17 条回复    2019-12-27 16:14:10 +08:00
    noreplay
        1
    noreplay   278 天前
    感觉好像是你没等数据下载完就开始解析了。当你解析的时候,data 还是一个“{}”,里面的所有字段都是空值,所以就报错了。
    elsagong
        2
    elsagong   278 天前
    @noreplay 我也觉得应该加个判别 null 的,这样在是 null 的时候就不会报错,但是不知道怎么加😂
    minigo
        3
    minigo   278 天前
    JArray columns = (JArray)jsonFeed["data"]["schema"]["fields"];
    ======>
    JArray columns = (JArray)jsonFeed["data"]?["schema"]?["fields"];
    guolaopi
        4
    guolaopi   278 天前
    断点看一下 try catch 之后的 historicalData 值就知道了
    noreplay
        5
    noreplay   278 天前
    假如只是判断 null 的话,有一个笨方法:
    代码大约是这个样子的:
    var feedData=(JArray)jsonFeed["data"];
    if(feedData!=null){
    // balabala
    }
    不过这样子的代码很丑陋就是了。
    crclz
        6
    crclz   278 天前
    不建议用 JArray JObject 实现。很容易出错。建议定义类来承载数据,再用 JsonConvert.Deserialize<T>(jsonString)解析。
    forgottencoast
        7
    forgottencoast   278 天前
    @noreplay #3 的才是正经写法,你大概不是 C#程序员。
    noreplay
        8
    noreplay   278 天前
    @forgottencoast 被看出来了,好尴尬。。。。。
    forgottencoast
        9
    forgottencoast   278 天前
    原因:
    从你说的(JArray)jsonFeed["data"]["schema"]["fields"];这样出错,
    但是上面一行没有出错,可以推断出是 schema 字段为 null,这个时候你再读取它的属性就会报空引用异常。

    处理:
    出现空引用异常的时候,你看一下服务端回传的 json 数据,一定是里面个别字段为空。
    这次是 schema,下次可能是别的,所以你要和服务端确认哪些可能为空,你如果用到的话,可能都需要额外处理的。
    elsagong
        10
    elsagong   278 天前
    @minigo 非常感谢,加```?```似乎真的有用,现在那行没有报错了,不过轮到```int rowNum = data.Count;
    int colNum = columns.Count;```了,实在不懂 C#,可以再咨询一下您吗😂万分感谢!!!!
    kkkkkrua
        11
    kkkkkrua   278 天前
    JArray data = (JArray)jsonFeed["data"]["data"];
    JArray columns = (JArray)jsonFeed["data"]["schema"]["fields"];
    -------
    JArray data = (JArray)jsonFeed["data"]?["data"] ?? new Jarray();
    JArray columns = (JArray)jsonFeed["data"]?["schema"]?["fields"] ?? new JArray();
    elsagong
        12
    elsagong   278 天前
    @kkkkkrua 再次感谢,无奈它还是会报错😂这一行``` range_head = range_head.get_Resize(1, colNum);
    ``` 报出的 System.Runtime.InteropServices.COMException HResult=0x800A03EC Message=异常来自 HRESULT:0x800A03EC,
    elsagong
        14
    elsagong   278 天前 via iPhone
    @yefuchao 感谢,我搜到了这个问题,但依然觉得是上面空值的代码问题,并非 Excel 版本的问题
    elsagong
        15
    elsagong   278 天前
    @guolaopi 断点看过了,感觉就是空值处理的问题(但是我不知道怎么处理😂),点击 Excel 函数弹窗时才会出现这个问题,如果在 Excel 里直接调用公式就没问题
    elsagong
        16
    elsagong   278 天前
    @noreplay 依然感谢你的解答,共同学习,共同进步☕️☕️
    elsagong
        17
    elsagong   278 天前
    @crclz 感谢你给到的思路,我去搜索以及摸索一下😂要被这个报错折磨疯了。。。。
    关于   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   1665 人在线   最高记录 5168   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 21ms · UTC 01:30 · PVG 09:30 · LAX 18:30 · JFK 21:30
    ♥ Do have faith in what you're doing.