Видалення полів зі структури або їх приховування в JSON Response

Я створив API у Go, який після виклику виконує запит, створює екземпляр struct, а потім кодує цю структуру як JSON, перш ніж відправляти його назад. Тепер я хотів би дозволити абоненту мати можливість вибирати конкретні поля, які вони хотіли б повернути, передавши в "полях" GET параметр.

Це означає, що, залежно від значення (полів) полів, моя структура зміниться. Чи є спосіб видалення полів зі структури? Або принаймні приховати їх у відповіді JSON динамічно? (Примітка: Іноді у мене є порожні значення, щоб тег JSON не містив тут). Заздалегідь спасибі.

Менша версія структур, які я використовую, нижче:

type SearchResult struct {
    Date        string      `json:"date"`
    IdCompany   int         `json:"idCompany"`
    Company     string      `json:"company"`
    IdIndustry  interface{} `json:"idIndustry"`
    Industry    string      `json:"industry"`
    IdContinent interface{} `json:"idContinent"`
    Continent   string      `json:"continent"`
    IdCountry   interface{} `json:"idCountry"`
    Country     string      `json:"country"`
    IdState     interface{} `json:"idState"`
    State       string      `json:"state"`
    IdCity      interface{} `json:"idCity"`
    City        string      `json:"city"`
} //SearchResult

type SearchResults struct {
    NumberResults int            `json:"numberResults"`
    Results       []SearchResult `json:"results"`
} //type SearchResults

Потім я кодую і виводжу відповідь так:

err := json.NewEncoder(c.ResponseWriter).Encode(&msg)
102
@Jacob, відповідно до оновленої відповіді PuerkitoBio, я думаю, що ви неправильно прочитали запитання. (Зараз) прийнято не може бути "правильною відповіддю" на ваше запитання, але відповідає запитуванню! Найвищий голосований відповідь може відповісти на ваше запитання, але повністю не застосовується до цього!
додано Автор Dave C, джерело

8 Відповіді

EDIT: I noticed a few downvotes and took another look at this Q&A. Most people seem to miss that the OP asked for fields to be dynamically selected based on the caller-provided list of fields. You can't do this with the statically-defined json struct tag.

Якщо потрібно завжди пропускати поле до json-encode, то, звичайно, використовуйте json: "-" , щоб ігнорувати поле (також зверніть увагу, що це <�сильне > not необхідне, якщо ваше поле не експортовано - ці поля завжди ігноруються кодером json). Але це не питання ОП.

Щоб навести коментар до відповіді json: "-" :

Це [відповідь json: "-" відповідь) - це відповідь, яку більшість людей, що закінчилися пошуком, хотіли б отримати, але це не відповідь на запитання.


У цьому випадку я б використав карту [string] interface {}. Поля можна легко видаляти, викликавши вбудований код delete на карті для видалення полів.

Тобто, якщо ви не можете запитувати лише для запитаних полів.

155
додано
Інша відповідь - це відповідь на це питання.
додано Автор Jacob, джерело
Це чудово спрацювало! Я тільки збираюся запитувати результати, які мені потрібні, тому мені не потрібно видаляти будь-які поля, які мені не потрібні, вони просто не створюватимуться. Я повинен був зробити деякі перевірки помилок, щоб гарантувати, що floats/integers/strings були призначені належним чином (IE: float значення 10.4 не повертається як "10.4" в JSON). Дякую!
додано Автор user387049, джерело
найімовірніше, ви не хочете повністю викидати ваше визначення типу. Це буде турбувати певну лінію, наприклад, коли ви хочете написати інші методи для цього типу, які мають доступ до цих полів. Використання проміжного інтерфейсу map [string] {} має сенс, але не вимагає викидання визначення типу.
додано Автор jorelli, джерело
Можливим недоліком видалення є те, що вам іноді може знадобитися підтримка декількох json-переглядів структури (map). Наприклад json перегляд для клієнта без чутливого поля і json перегляд для бази даних З чутливим полем. На щастя, досі можна використовувати структуру - просто подивіться на мою відповідь.
додано Автор Adam Kurkiewicz, джерело

використовувати `json:" - "`

// Field is ignored by this package.
Field int `json:"-"`

// Field appears in JSON as key "myName".
Field int `json:"myName"`

// Field appears in JSON as key "myName" and
// the field is omitted from the object if its value is empty,
// as defined above.
Field int `json:"myName,omitempty"`

// Field appears in JSON as key "Field" (the default), but
// the field is skipped if empty.
// Note the leading comma.
Field int `json:",omitempty"`

doc : http://golang.org/pkg/encoding/json/#Marshal

120
додано
Це відповідь, яку більшість людей, що закінчилися пошуками, захочуть, але це не відповідь на це питання.
додано Автор Filip Haglund, джерело
тільки що я шукав +1
додано Автор pregmatch, джерело
Як вже було сказано, ОП просив метод динамічно формувати DTO.
додано Автор codepushr, джерело
Я не згоден з @Jacob, тому що оператор заявив, що вони хочуть динамічно керувати полями виводу на основі записів рядків запитів до API. Наприклад, якщо абонент, який викликає API, запитує лише про індустрію та країну, вам доведеться видалити решту. Ось чому відповідь на це питання позначена як відповідь на це питання. Ця високопроголошена відповідь полягає в маркуванні полів, явно ніколи не доступних для будь-якого вбудованого json-marshaler - EVER. якщо ви хочете його динамічно, відповідь на відмітку - це відповідь.
додано Автор eduncan911, джерело

Інший спосіб зробити це - мати структуру покажчиків із тегом , omitempty . Якщо вказівники є nil , поля не буде виставлено.

Цей метод не вимагатиме додаткового відображення або неефективного використання карт.

Same example as jorelli using this method: http://play.golang.org/p/JJNa0m2_nw

37
додано
Звичайно, цей метод вимагає рефлексії, jss-to-struct маршалювання stdlib завжди використовує відображення (насправді він завжди використовує період відображення, карту або структуру або що завгодно).
додано Автор mna, джерело
+1 Повністю згоден. Я використовую це правило/трюк весь час з вбудованими маршалами (і навіть побудував читач/письменник CSV, заснований від цього правила, а також! Оператор може просто не встановити значення * Country на нуль, і воно буде опущено. І дивовижно, що ви поставили хороший, y набрав play.golang, а також.
додано Автор eduncan911, джерело
Так, але це не вимагає додаткового відображення з використанням інтерфейсів, які рекомендують інші відповіді.
додано Автор Druska, джерело

Пакет reflect можна використовувати для вибору потрібних полів, відображаючи теги полів і вибираючи значення тега json . Визначте метод у типі SearchResults, який вибирає потрібні поля та повертає їх як map [string] interface {} , а потім маршал що замість структури SearchResults . Ось приклад того, як можна визначити цей метод:

func fieldSet(fields ...string) map[string]bool {
    set := make(map[string]bool, len(fields))
    for _, s := range fields {
        set[s] = true
    }
    return set
}

func (s *SearchResult) SelectFields(fields ...string) map[string]interface{} {
    fs := fieldSet(fields...)
    rt, rv := reflect.TypeOf(*s), reflect.ValueOf(*s)
    out := make(map[string]interface{}, rt.NumField())
    for i := 0; i < rt.NumField(); i++ {
        field := rt.Field(i)
        jsonKey := field.Tag.Get("json")
        if fs[jsonKey] {
            out[jsonKey] = rv.Field(i).Interface()
        }
    }
    return out
}

and here's a runnable solution that shows how you would call this method and marshal your selection: http://play.golang.org/p/1K9xjQRnO8

11
додано
Я намагаюся триматися подалі від роздумів, але це дуже добре зберігає інформацію про тип ... Приємно мати код, який документує те, як виглядають ваші структури краще, ніж купу тегів if/else у методі validate() (якщо ви навіть мати)
додано Автор Aktau, джерело
Подумайте про це, ви могли б розумно узагальнити шаблон вибору полів до будь-якого типу і будь-якого ключа тегу; немає нічого про це, що є специфічним для визначення SearchResult або ключа json.
додано Автор jorelli, джерело

Візьміть три інгредієнти:

  1. Пакет відображає цикл для всіх полів структури.

  2. Заяву if , щоб вибрати поля, які потрібно Marshal , і

  3. Пакет encoding/json для Marshal полів, які вам подобаються.

Підготовка:

  1. Змішайте їх у гарній пропорції. Використовуйте reflect.TypeOf (your_struct). Field (i) .Name() , щоб отримати ім'я i -го поля your_struct .

  2. Використовуйте reflect.ValueOf (your_struct). Поле (i) , щоб отримати тип Значення представлення i -го поля your_struct.

  3. Використовуйте fieldValue.Interface() , щоб отримати фактичне значення (приховане до інтерфейсу типу {}) fieldValue типу Value (зверніть увагу на використання дужок - метод інтерфейсу() створює interface {}

Якщо вам не вдасться спалити транзистори або вимикачі в процесі, ви повинні отримати щось на зразок цього:

func MarshalOnlyFields(structa interface{},
    includeFields map[string]bool) (jsona []byte, status error) {
    value := reflect.ValueOf(structa)
    typa := reflect.TypeOf(structa)
    size := value.NumField()
    jsona = append(jsona, '{')
    for i := 0; i < size; i++ {
        structValue := value.Field(i)
        var fieldName string = typa.Field(i).Name
        if marshalledField, marshalStatus := json.Marshal((structValue).Interface()); marshalStatus != nil {
            return []byte{}, marshalStatus
        } else {
            if includeFields[fieldName] {
                jsona = append(jsona, '"')
                jsona = append(jsona, []byte(fieldName)...)
                jsona = append(jsona, '"')
                jsona = append(jsona, ':')
                jsona = append(jsona, (marshalledField)...)
                if i+1 != len(includeFields) {
                    jsona = append(jsona, ',')
                }
            }
        }
    }
    jsona = append(jsona, '}')
    return
}

Обслуговування:

служити з довільною структурою і map [string] bool полів, які потрібно включити, наприклад

type magic struct {
    Magic1 int
    Magic2 string
    Magic3 [2]int
}

func main() {
    var magic = magic{0, "tusia", [2]int{0, 1}}
    if json, status := MarshalOnlyFields(magic, map[string]bool{"Magic1": true}); status != nil {
        println("error")
    } else {
        fmt.Println(string(json))
    }

}

Смачного!

5
додано
Увага! Якщо ваші includeFields містять імена полів, які не збігаються з фактичними полями, ви отримаєте недійсний json. Вас попередили.
додано Автор Adam Kurkiewicz, джерело

Я щойно опублікував шериф , який перетворює структури на карту на основі міток, позначених на полях структури. Потім ви можете створити (JSON або інші) згенеровану карту. Це, ймовірно, не дозволяє вам тільки серіалізувати набір полів, які запитувач вимагав, але я думаю, що використання набору груп дозволить вам охопити більшість випадків. Використання груп замість полів безпосередньо, швидше за все, також збільшить можливість кешування.

Приклад:

package main

import (
    "encoding/json"
    "fmt"
    "log"

    "github.com/hashicorp/go-version"
    "github.com/liip/sheriff"
)

type User struct {
    Username string   `json:"username" groups:"api"`
    Email    string   `json:"email" groups:"personal"`
    Name     string   `json:"name" groups:"api"`
    Roles    []string `json:"roles" groups:"api" since:"2"`
}

func main() {
    user := User{
        Username: "alice",
        Email:    "[email protected]",
        Name:     "Alice",
        Roles:    []string{"user", "admin"},
    }

    v2, err := version.NewVersion("2.0.0")
    if err != nil {
        log.Panic(err)
    }

    o := &sheriff.Options{
        Groups:     []string{"api"},
        ApiVersion: v2,
    }

    data, err := sheriff.Marshal(o, user)
    if err != nil {
        log.Panic(err)
    }

    output, err := json.MarshalIndent(data, "", "  ")
    if err != nil {
        log.Panic(err)
    }
    fmt.Printf("%s", output)
}
3
додано
спасибі за цю відповідь, дуже корисна бібліотека!
додано Автор weisjohn, джерело

Ви можете використовувати атрибут позначення "omitifempty" або робити необов'язкові покажчики полів і залишити ті, які ви хочете пропустити, неініціалізовані.

2
додано
Це найбільш правильна відповідь на запитання та випадок використання ОП.
додано Автор user1943442, джерело
Ви маєте на увазі "omitempty". Див. golang.org/pkg/encoding/json/#Marshal
додано Автор Matthew Ratzloff, джерело
@ user1943442, це не так; ОР <�явно згадує, чому "омилепите" неприпустимо.
додано Автор Dave C, джерело

Тепер питання трохи застаріло, але я натрапив на те ж саме питання ще недавно, і, оскільки я не знайшов легкого способу зробити це, я побудував бібліотеку, що виконує цю мету. Це дозволяє легко генерувати map [string] interface {} зі статичної структури.

https://github.com/tuvistavie/structomap

1
додано
Тепер ви можете легко зробити це, використовуючи фрагмент коду з мого рецепту.
додано Автор Adam Kurkiewicz, джерело
Фрагмент є підмножиною бібліотеки, але головне питання про повернення [] байта полягає в тому, що це не дуже багаторазове використання: наприклад, неможливо легко додати поле. Тому я пропоную створити map [string] interface {} і дозвольте частині JSON для серіалізації до стандартної бібліотеки.
додано Автор Daniel Perez, джерело
ІТ КПІ - JavaScript
ІТ КПІ - JavaScript
504 учасників

співтовариство javascript розробників в Telegram

IT KPI Go
IT KPI Go
59 учасників