Excel 中的 JSON 重复输入部分

Posted

技术标签:

【中文标题】Excel 中的 JSON 重复输入部分【英文标题】:JSON repeating inputs section from Excel 【发布时间】:2019-02-20 20:42:25 【问题描述】:

我有一个特定的 JSON 字符串,我需要匹配它以进行休息调用。我正在从 Excel 电子表格中提取数据。其中一个部分具有重复输入,如下所示。我的电子表格中的数据如下所示:

我需要生成的 JSON 如下所示:

"detailInputs": [
  
    "name": "SOGrid",
    "repeatingInputs": [
        
            "inputs": [
                
                    "name": "ItemNumber",
                    "value": "XYZ"
                ,
                
                    "name": "Quantity",
                    "value": "1"
                
            ]
        ,
        
            "inputs": [
                
                    "name": "ItemNumber",
                    "value": "ABC"
                ,
                
                    "name": "Quantity",
                    "value": "3"
                
            ]
        
    ]

到目前为止我尝试过的如下(注意 jsonArraystring 是上一节中格式化的标头信息):

using (var conn = new OleDbConnection(connectionString))

    sheetName = "Detail";
    conn.Open();
    var cmd = conn.CreateCommand();
    cmd.CommandText = $"SELECT * FROM [sheetName$]";

    using (var rdr = cmd.ExecuteReader())
    
        var query = rdr.Cast<DbDataRecord>().Select(row => new 
            name = row[0],
            value = row[1],
            //description = row[2]
        );

        var json = JsonConvert.SerializeObject(query);
        jsonArrayString = jsonArrayString + ",\"detailInputs\":[\"name\":\"SOGrid\",\"repeatingInputs\":[\"inputs\": " + json + "]]";

这非常接近,但将“重复输入”都放在一个输入部分中。

我还尝试将值分配给字典和列表,希望从中提取适当的对并格式化 JSON,这是开始,但我对解开键值对以获得格式正确。

using (var conn = new OleDbConnection(connectionString))

    sheetName = "Detail";
    conn.Open();
    int counter = 0;
    var cmd = conn.CreateCommand();
    cmd.CommandText = $"SELECT * FROM [sheetName$]";
    var values = new List<Dictionary<string, object>>();
    var ListValues = new List<string>();

    using (var rdr = cmd.ExecuteReader())
    
        while (rdr.Read())
        
            var fieldValues = new Dictionary<string, object>();
            var fieldValuesList = new List<string>();

            for (int i = 0; i < rdr.FieldCount; i++)
            
                fieldValues.Add(rdr.GetName(i), rdr[i]);
                fieldValuesList.Add(rdr.GetName(i));
            

            // add the dictionary on the values list
            values.Add(fieldValues);
        

根本问题是如何通过从 excel 数据中提取数据来创建重复输入结构,如 JSON 示例中所示。

【问题讨论】:

【参考方案1】:

您要做的是将 Excel 工作表的内容序列化为 "repeatingInputs" 属性的数组值,使用特定的结构。我建议将其分解为一系列 LINQ 转换。

首先介绍几个扩展方法:

public static class DataReaderExtensions

    // Adapted from this answer https://***.com/a/1202973
    // To https://***.com/questions/1202935/convert-rows-from-a-data-reader-into-typed-results
    // By https://***.com/users/3043/joel-coehoorn
    public static IEnumerable<T> SelectRows<T>(this IDataReader reader, Func<IDataRecord, T> select)
    
        while (reader.Read())
        
            yield return select(reader);
        
    


public static class EnumerableExtensions

    // Adapted from this answer https://***.com/a/419058
    // To https://***.com/questions/419019/split-list-into-sublists-with-linq/
    // By https://***.com/users/50776/casperone
    public static IEnumerable<List<T>> ChunkWhile<T>(this IEnumerable<T> enumerable, Func<List<T>, T, bool> shouldAdd)
    
        if (enumerable == null || shouldAdd == null)
            throw new ArgumentNullException();
        return enumerable.ChunkWhileIterator(shouldAdd);
    

    static IEnumerable<List<T>> ChunkWhileIterator<T>(this IEnumerable<T> enumerable, Func<List<T>, T, bool> shouldAdd)
    
        List<T> list = new List<T>();
        foreach (var item in enumerable)
        
            if (list.Count > 0 && !shouldAdd(list, item))
            
                yield return list;
                list = new List<T>();
            
            list.Add(item);
        
        if (list.Count != 0)
        
            yield return list;
        
    

第一种方法将IDataReader 打包到一个可枚举的类型对象中,每行一个。这样做可以更轻松地将数据读取器的内容提供给后续的 LINQ 转换。第二种方法基于某些谓词条件将平面可枚举分解为列表“块”的可枚举。这将用于在每个 ItemNumber 行将行分成块。

使用这两种扩展方法我们可以生成所需的JSON,如下所示:

public static string ExtractRows(string connectionString, string sheetName)

    using (var conn = new OleDbConnection(connectionString))
    
        conn.Open();
        using (var cmd = conn.CreateCommand())
        
            cmd.CommandText = string.Format("SELECT * FROM [0]", sheetName);
            using (var rdr = cmd.ExecuteReader())
            
                var query = rdr
                    // Wrap the IDataReader in a LINQ enumerator returning an array of key/value pairs for each row.
                    // Project the first two columns into a single anonymous object.
                    .SelectRows(r =>
                    
                        // Check we have two columns in the row, and the first (Name) column value is non-null.
                        // You might instead check that we have at least two columns.
                        if (r.FieldCount != 2 || r.IsDBNull(0))
                            throw new InvalidDataException();
                        return new  Name = r[0].ToString(), Value = r[1] ;
                    )
                    // Break the columns into chunks when the first name repeats
                    .ChunkWhile((l, r) => l[0].Name != r.Name)
                    // Wrap in the container Inputs object
                    .Select(r => new  Inputs = r );

                // Serialize in camel case
                var settings = new JsonSerializerSettings
                
                    ContractResolver = new CamelCasePropertyNamesContractResolver(),
                ;
                return JsonConvert.SerializeObject(query, Formatting.Indented, settings);
            
        
    

这将为"repeatingInputs" 生成所需的值:

[
  
    "inputs": [
      
        "name": "ItemNumber",
        "value": "XYZ"
      ,
      
        "name": "Quantity",
        "value": "1"
      
    ]
  ,
  
    "inputs": [
      
        "name": "ItemNumber",
        "value": "ABC"
      ,
      
        "name": "Quantity",
        "value": "3"
      
    ]
  
]

【讨论】:

这非常有效。感谢您提供非常优雅的解决方案和很好的解释。 @bairdmar - 不客气。顺便说一句,在你的代码中我注意到你做了rdr.Cast&lt;DbDataRecord&gt;()。是否有任何文档 DbDataReader.GetEnumerator.GetEnumerator() 实际枚举的内容?枚举器本身是无类型的。 DbDataRecordIDataRecord 似乎都是合理的。 没有我见过的文档。我实际上是在某个时候从 *** 帖子中找到的。阅读有关 IDataRecord 的文档,这似乎是一种更准确的用法,但 DBDataRecord 的行为是相同的,因为 DBDataRecord 实现了 IDataRecord。 docs.microsoft.com/en-us/dotnet/api/…

以上是关于Excel 中的 JSON 重复输入部分的主要内容,如果未能解决你的问题,请参考以下文章

如何删除两个excel表中的重复数据

如何快速查找两个表格中的重复数据。

excel提取单元格中的部分内容

熊猫,附加到excel中的下一个可用行[重复]

excel表格要讲筛选出的部分内容整页打印出来要怎么实现?

excel怎么样自动选择重复项中的唯一值