Как считать заранее неизвестную таблицу из базы данных в golang?
Уже поздно. Пока появилась такая мысль.

Попробуйте сначала выполнить запрос
SHOW COLUMNS FROM mytableдля получения имен полей и их типов.
Rows.Columns для получения имен полей и их порядка. Получим []interface{}.

Затем, зная имена полей, можно получать результаты при помощи SELECT col1, ... colN FROM mytable и при обработке rows.Next()получать сами данные, которые заполнять уже в массив структур.
Соответственно, сами структуры уже сериализовывать в нужный формат.
func sqlToJson(res *sql.Rows) ([]byte, error) {
	// создаем массив названий столбцов таблицы
	arrNamesColumns, _ := res.Columns()

	// получаем количество столбцов
	kolColumns := len(arrNamesColumns)

	// создаем отображения которое по ключу (названию столбца) будет хранить срез всех записей данного столбца
	resMap := make(map[string][]interface{}, kolColumns)

	// создаем срез который будет хранить все поля текущий строки таблицы в массиве байт
	tempLineByte := make([][]byte, kolColumns)

	// создаем срез который будет хранить ссылки на поля предыдущего среза
	// это нужно для метода Scan который принимает interface по ссылке таким образом записывая в него данные
	pTempLineByte := make([]interface{}, kolColumns)
	for i := 0; i < kolColumns; i++ {
		pTempLineByte[i] = &tempLineByte[i]
	}

	// создаем срез который будет хранить все поля текущий строки в соответствующим типе данных
	tempLine := make([]interface{}, kolColumns)

	// перебираем все строки тоблицы
	for res.Next() {
		// метод Scan записывает в срез tempLineByte текущую строку через срез pTempLineByte который хранит ссылки на поля tempLineByte
		err := res.Scan(pTempLineByte...)
		if err != nil {
			return nil, err
		}

		// перебираем все поля текущей строки
		for i := 0; i < kolColumns; i++ {
			// преобразовываем байтовое значение в значение соответствующего формата
			json.Unmarshal(tempLineByte[i], &tempLine[i])

			// если записалось значение nil то в поле была строка
			if tempLine[i] == nil {
				// добавляем значения в соответствующий ключу срез отображения resMap преобразуя его в строку
				resMap[arrNamesColumns[i]] = append(resMap[arrNamesColumns[i]], string(tempLineByte[i]))
			} else {
				// иначе добавлять в срез соответствующие значения tempLine
				resMap[arrNamesColumns[i]] = append(resMap[arrNamesColumns[i]], tempLine[i])
			}
		}
	}

	// преобразовываем отображения в json и возвращаем в массиве байт
	return json.Marshal(resMap)

	// p.s. чтобы получить привычный json нужно просто преобразовать этот массив байт в строку
	// также, может понадобится убрать из этой строки символы переноса \n которые могут быть в текстовых полях базы данных, так как с ними json считается неликвидным
}