'Creating a JSON object from Google Sheets

I am trying to learn AppScript and I am using Google Sheets as an example. I want to create a simple JSON object using some data populated in the Sheet.

Table Example

Name    ID   Price   Qty
ABC     123  100      1
DEF     342   56      2
HIJ     233   90      3
IJK     213   68      5

I want the JSON out to be something like

[
  {
    "Name": "ABC",
    "ID": "123",
    "Price": 100,
    "Qty": 1
  },
  {
    "Name": "DEF",
    "ID": "342",
    "Price": 56,
    "Qty": 2
  },
  {
    "Name": "HIJ",
    "ID": "233",
    "Price": 90,
    "Qty": 3
  },
  {
    "Name": "IJK",
    "ID": "213",
    "Price": 68,
    "Qty": 5
  }
]

I started by following this Youtube tutorial : https://www.youtube.com/watch?v=TQzPIVJf6-w. However that video talks about creating each column header as a the object. Where as i want the Column name to be the key and the Row value to be the value.

Here is my current AppScript Code

function doGet() {
  var result={};
  var rewards = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1')
  .getDataRange()
  .getValues();

  result.rewardObj = makeObject(rewards);
  //Logger.log(result.rewardObj);
  return ContentService.createTextOutput(JSON.stringify(result))
  .setMimeType(ContentService.MimeType.JSON)


}

function makeObject(multiArray)
{
  var obj = {}; 
  var colNames = multiArray.shift();
  var rowNames = multiArray.slice(0,1);
  var rowCount = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1').getLastRow();
  var colCount = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1').getLastColumn();

  for(var j=0;j<4;j++)
  {
    for(var i=0;i<4;i++)
      {
        //obj[colNames] = rowNames.map(function(item){return item[i];});
        obj[colNames[j][i]] = multiArray[j][i];
      }
  }
  Logger.log(rowCount)
  Logger.log(colCount)
  Logger.log(multiArray[57][12]);
  return obj;
}

The output is a single object

{"rewardObj":{"Name":"ABC","ID":"123","Price":"100","Qty":"1"}}

PS: I am not a programmer, I am just learning some scripting in a hackey way. Apologies for not knowing the basics :)



Solution 1:[1]

I know it's been a while, but I came up with a slightly more flexible solution. I've created an aptly named function that converts spreadsheet data to objects, given a set of headers and a set of rows.

function convertToObjects(headers, rows)
{
  return rows.reduce((ctx, row) => {
    ctx.objects.push(ctx.headers.reduce((item, header, index) => {
      item[header] = row[index];
      return item;
    }, {}));
    return ctx;
  }, { objects: [], headers}).objects;
}

You can call the above function like this:

var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('YourSheetName');
var [headers, ...rows] = sheet.getDataRange().getValues();
var objects = convertToObjects(headers, rows);

If you need to ignore the first row and get headers from the second row (like I do), then you can do this instead:

var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('YourSheetName');
var [_, headers, ...rows] = sheet.getDataRange().getValues();
var objects = convertToObjects(headers, rows);

If you have any improvements to this, please let me know in the comments below.

Solution 2:[2]

edit 2022 : for headers of columns and headers of rows as follows

yourJsonName col1 col2 col3 col4
row1 1 2 3
row2 4 6 4
row3 7 8 9 8

try

function myJson() {
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('YourSheetName');
  var [headers, ...rows] = sheet.getDataRange().getValues();
  var jsonString = `var ${sheet.getRange(1,1).getValue()} = ${JSON.stringify(data2json(headers, rows))}`
  Logger.log (jsonString)
}
function data2json(headers, rows) {
  var obj = {},items = {};
  for (var i in rows) {
    obj = {};
    for (var col = 1; col < headers.length; col++) {
      if(rows[i][col]!='') obj[headers[col]] = rows[i][col];
    }
    items[rows[i][0]] = obj
  }
  return items;
}

var [headers, ...rows] = [['yourJsonName', 'col1', 'col2', 'col3', 'col4'], ['row1', 1.0, 2.0, 3.0, ], ['row2', 4.0, , 6.0, 4.0], ['row3', 7.0, 8.0, 9.0, 8.0]]
console.log (`var ${headers[0]} = ${JSON.stringify(data2json(headers, rows))}`)
function data2json(headers, rows) {
  var obj = {},items = {};
  for (var i in rows) {
    obj = {};
    for (var col = 1; col < headers.length; col++) {
      if(rows[i][col]!='') obj[headers[col]] = rows[i][col];
    }
    items[rows[i][0]] = obj
  }
  return items;
}

with saving in drive ... adapt names as necessary (YourSheetName, YourFolderName, YourFileName)

function myJson() {
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('YourSheetName');
  var [headers, ...rows] = sheet.getDataRange().getValues();
  var jsonString = `var ${sheet.getRange(1,1).getValue()} = ${JSON.stringify(data2json(headers, rows))}`
  var folders = DriveApp.getFoldersByName('YourFolderName');
  if (folders.hasNext()) {
    var folder = folders.next();
    saveData(folder, 'YourFileName.json',jsonString);
  }
}
function data2json(headers, rows) {
  var obj = {},items = {};
  for (var i in rows) {
    obj = {};
    for (var col = 1; col < headers.length; col++) {
      if(rows[i][col]!='') obj[headers[col]] = rows[i][col];
    }
    items[rows[i][0]] = obj
  }
  return items;
}
function saveData(folder, fileName, content) {
  var children = folder.getFilesByName(fileName);
  var file = null;
  if (children.hasNext()) {
    file = children.next();
    file.setContent(content);
  } else {
    file = folder.createFile(fileName, content);
  }
  Logger.log(file.getUrl())
}

Solution 3:[3]

I, too, ran into this issue. The answer lies within your Startup.cs file.

First, the reason for which the "Invalid 'Content-Type' header" appears even while using GraphQL.Upload is because (at the time of this answer) the GraphQL.net middleware only checks for the three different Content-Type headers and errors out if none of those match. See GitHub

As for the solution, you haven't shared any of your Startup.cs file so I'm not sure what yours looks like. I'll share the relevant pieces of mine.

You'll need to add the service and the middleware.

The service:

services.AddGraphQLUpload()
  .AddGraphQL((options, provider) =>
  {
  ...
  });

If you're using a endpoints to map to a middleware, you'll need to add the UseGraphQLUpload middleware, then map your endpoint.

Example:

  app.UseGraphQLUpload<YourSchema>("/api/graphql", new 
  GraphQLUploadOptions {
    UserContextFactory = (ctx) => new GraphQlUserContext(ctx.User)
  });
  app.UseEndpoints(endpoints =>
  {
    // map HTTP middleware for YourSchema at path api/graphql
    endpoints
      .MapGraphQL<YourSchema, GraphQLMiddleware<YourSchema>>("api/graphql")
      .RequireAuthorization();
    ...
    // Additional endpoints
    ...
   }

Everything else looks fine to me.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3 Dharman