Excel中的JSON重复输入部分

时间:2019-02-20 20:42:26

标签: c# json

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

ExcelData

我需要生成的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 个答案:

答案 0 :(得分:2)

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

首先,介绍几种扩展方法:

public static class DataReaderExtensions
{
    // Adapted from this answer https://stackoverflow.com/a/1202973
    // To https://stackoverflow.com/questions/1202935/convert-rows-from-a-data-reader-into-typed-results
    // By https://stackoverflow.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://stackoverflow.com/a/419058
    // To https://stackoverflow.com/questions/419019/split-list-into-sublists-with-linq/
    // By https://stackoverflow.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"
      }
    ]
  }
]