Каков наиболее эффективный способ «фильтрации» объектов JSON из пары ключ-значение? - PullRequest
0 голосов
/ 25 сентября 2018

Я читаю в .json файле.Это массив объектов в допустимом формате JSON, например:

    [
        {
                "Id": 13,
                "Location": "Australia",
                "Content": "Another string"
        },
        {
                "Id": 145,
                "Location": "England",
                "Content": "SomeString"
        },
        {
                "Id": 12,
                "Location": "England",
                "Content": "SomeString"
        },
        {
                "Id": 12331,
                "Location": "Sweden",
                "Content": "SomeString"
        },
        {
                "Id": 213123,
                "Location": "England",
                "Content": "SomeString"
        }
     ]

Я хочу отфильтровать эти объекты - скажем, удалить все, где "Location" не равно "England".

То, что я пробовал до сих пор, - это создание пользовательской функции UnmarshalJSON.Он демарширует его, но объекты, которые он генерирует, пусты - и столько же, сколько и входные данные.

Пример кода:

type languageStruct struct {
    ID                  int     `json:"Id"`
    Location            string  `json:"Location"` 
    Content             string  `json:"Content"`
}

func filterJSON(file []byte) ([]byte, error) {
    var x []*languageStruct

    err := json.Unmarshal(file, &x)
    check(err)

    return json.MarshalIndent(x, "", " ")
}


func (s *languageStruct) UnmarshalJSON(p []byte) error {

    var result struct {
        ID              int     `json:"Id"`
        Location        string  `json:"Location"` 
        Content         string  `json:"Content"`
    }

    err := json.Unmarshal(p, &result)
    check(err)

    // slice of locations we'd like to filter the objects on
    locations := []string{"England"} // Can be more 

    if sliceContains(s.Location, locations) {
        s.ID = result.ID
        s.Location= result.Location
        s.Content = result.Content
    }

    return nil
}

// helper func to check if a given string, f.e. a value of a key-value pair in a json object, is in a provided list
func sliceContains(a string, list []string) bool {
    for _, b := range list {
        if b == a {
            fmt.Println("it's a match!")
            return true
        }
    }
    return false
}

Пока это выполняется - вывод неправильный.Он создает столько объектов, сколько приходит, но новые пустые, например:

// ...
 [
 {
  "Id": 0,
  "Location": "",
  "Content": ""
 },
 {
  "Id": 0,
  "Location": "",
  "Content": ""
 }
 ]
//...

В то время как мой желаемый результат из первого заданного ввода будет:

[
    {
            "Id": 145,
            "Location": "England",
            "Content": "SomeString"
    },
    {
            "Id": 12,
            "Location": "England",
            "Content": "SomeString"
    },
    {
            "Id": 213123,
            "Location": "England",
            "Content": "SomeString"
    }
 ]

1 Ответ

0 голосов
/ 25 сентября 2018

Когда вызывается languageStruct.UnmarshalJSON(), уже подготовлено languageStruct, которое будет добавлено к срезу, независимо от того, заполняете ли вы его содержимое (поля) или нет.

Самый простой и мой предложенныйРешение состоит в том, чтобы просто распаковать и обработать срез: удалить элементы в соответствии с вашими требованиями.Это приводит к чистому коду, который вы можете легко настроить / изменить в будущем.Хотя она может быть реализована как пользовательская логика маршалинга для пользовательского типа среза []languageStruct, я все равно не буду создавать для этого собственную логику маршалинга, а реализовывать ее как отдельную логику фильтрации.

Вот простой код демаршалинга, фильтрацияи маршалинг снова (примечание: для этого не определено / не используется пользовательский маршалинг):

var x []*languageStruct

err := json.Unmarshal(file, &x)
if err != nil {
    panic(err)
}

var x2 []*languageStruct
for _, v := range x {
    if v.Location == "England" {
        x2 = append(x2, v)
    }
}

data, err := json.MarshalIndent(x2, "", " ")
fmt.Println(string(data), err)

Это приведет к желаемому результату.Попробуйте это на Go Playground .

Самым быстрым и самым сложным решением было бы использовать управляемый событиями анализ и построение конечного автомата, но сложность увеличитсяпо большому счетуИдея состоит в том, чтобы обрабатывать JSON с помощью токенов, отслеживать, где вы находитесь в данный момент в дереве объектов, и когда обнаруживается объект, который необходимо исключить, не обрабатывайте / не добавляйте его в свой фрагмент.Для получения подробной информации и идей, как это можно написать, проверьте этот ответ: Go - декодируйте JSON, поскольку он все еще передается через net / http

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