Unmarshal Json Массив объекта, полученный из postgresql - PullRequest
1 голос
/ 30 октября 2019

У меня есть таблица в Postgres, которая является Jsonb

Create Table Business(
    id serial not null primary key,
    id_category integer not null,
    name varchar(50) not null,
    owner varchar(200) not null,
    coordinates jsonb not null,
    reason varchar(300) not null,
    foreign key(id_category) references Category(id)
);

, как вы можете видеть, что я храню координаты как jsonb

ex:

Insert into Business(id_category, name, owner, coordinates, reason) 
values 
(1,'MyName','Owner', [{"latitude": 12.1268142, "longitude": -86.2754}]','Description')

способ, которым я извлекаю данные и назначаю их, выглядит следующим образом.

type Business struct {
    ID int `json:"id,omitempty"`
    Name string `json:"name,omitempty"`
    Owner string `json:"owner,omitempty"`
    Category string `json:"category,omitempty"`
    Departments []string `json:"departments,omitempty"`
    Location []Coordinates `json:"location,omitempty"`
    Reason string `json:"reason,omitempty"`
}

type Coordinates struct {
    Latitude float64 `json:"latitude,omitempty"`
    Longitude float64 `json:"longitude,omitempty"`
}

func (a Coordinates) Value() (driver.Value, error) {
    return json.Marshal(a)
}

func (a *Coordinates) Scan(value []interface{}) error {
    b, ok := value.([]byte)
    if !ok {
        return errors.New("type assertion to []byte failed")
    }
    return json.Unmarshal(b, &a)
}

Однако я продолжаю получать это сообщение.

sql: ошибка сканирования по индексу столбца 3, имя «координаты»: не поддерживается сканирование, сохранение драйвера. Тип значения [] uint8 для типа * models.Coordinates

ИКонтроллер, который я использую для извлечения информации:

func (b *BusinessRepoImpl) Select() ([]models.Business, error) {
    business_list := make([]models.Business, 0)
    rows, err := b.Db.Query("SELECT business.id, business.name, business.owner, business.coordinates, business.reason_froggy, category.category FROM business INNER JOIN category on category.id = business.id_category group by business.id, business.name, business.owner, business.coordinates, business.reason_froggy, category.category")
    if err != nil {
        return business_list, err
    }
    for rows.Next() {
        business := models.Business{}
        err := rows.Scan(&business.ID, &business.Name, &business.Owner, &business.Location, &business.Reason, &business.Category)
        if err !=  nil {
            break
        }
        business_list = append(business_list, business)
    }
    err = rows.Err()
    if err != nil {
        return business_list, err
    }
    return business_list, nil
}

Может кто-нибудь сказать, пожалуйста, как решить эту проблему? Получите массив объектов json и присвойте его полю координат в Business.

Ответы [ 2 ]

0 голосов
/ 31 октября 2019

Я знаю, что это решение на самом деле неоптимизировано, но это был единственный способ, которым оно работает.

В основном мне нужно получить json, а затем выполнить демаршал в атрибут Location.

var location string = ""

if err := json.Unmarshal([]byte(location), &business.Location); err != nil { panic(err) }
0 голосов
/ 31 октября 2019

1.

Как видно из документации, для интерфейса Scanner требуется метод

Scan(src interface{}) error

Но ваш тип *Coordinates реализует другой метод

Scan(value []interface{}) error

Типы interface{} и []interface{} - это две совершенно разные вещи.

2.

Интерфейс сканера должен быть реализован для типа поля, которое вы хотите передать в качестве аргумента rows.Scan. То есть вы реализовали свой метод Scan на *Coordinates, но тип поля Location равен []Coordinates.

Опять-таки, типы *Coordinates и []Coordinates - это дваочень разные вещи.


Таким образом, решение состоит в том, чтобы реализовать интерфейс правильно и с правильным типом.

Обратите внимание, что поскольку Go не позволяет добавлять методы к неназванным типам, и []Coordinates - это безымянный тип, вам нужно объявить новый тип, который вы затем будете использовать вместо []Coordinates.

type CoordinatesSlice []Coordinates

func (s *CoordinatesSlice) Scan(src interface{}) error {
    switch v := src.(type) {
    case []byte:
        return json.Unmarshal(v, s)
    case string:
        return json.Unmarshal([]byte(v), s)
    }
    return errors.New("type assertion failed")
}

// ...

type Business struct {
    // ...
    Location CoordinatesSlice `json:"location,omitempty"`
    // ...
}

ПРИМЕЧАНИЕ

Если в бизнес-местоположении всегда будет только одна пара координат, сохраните их в db как объект jsonb и измените тип Location с CoordinatesSlice на Coordinates исоответственно переместите реализацию Scanner с *CoordinatesSlice на *Coordinates.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...