Go语言 如何反编组类型为的递归json

hujrc8aj  于 2023-03-06  发布在  Go
关注(0)|答案(2)|浏览(137)

我得到了一个JSON,格式如下:

{
  "Filter": {
    "Filters": [
      {
        "Filter": {
           "someKey": "someValue1",
        },
        "FilterType": "trait"
      },
      {
        "Filter": {
           "someKey": "someValue2",
        },
        "FilterType": "trait"
      }
    ]
  },
  "FilterType": "and" // this can be an or too
}

每一个过滤器都有一个类型Map到我在代码中得到的一个特定的结构体,正如你所看到的,这有一个递归的特性:滤波器可以是具有多个子滤波器的and(其也可以是andor等)。
所以,根据类型的不同,我需要递归地将它Map到正确的结构体。有什么建议吗?我考虑过使用mapstructure,但即使这样代码也会变得相当糟糕。
请注意,本示例仅使用andtrait过滤器,但假设存在更多类似or的过滤器

l0oc07j2

l0oc07j21#

这种类型的多态JSON很常见,而且通常不容易处理。我在这里解释的是一种使用单独的结构体来处理它的方法,该结构体仅用于封送,并依赖于后处理来构造过滤器。
您可以定义一个FilterMarshal结构,其中包含筛选器的所有可能字段:

type FilterMarshal struct {
   FilterType string `json:"FilterType"`
   Filters []*FilterMarshal `json:"Filters"`
   Filter   *FilterMarshal `json:"Filter"`
   SomeKey  string `json:"someKey"`
   ...
}

这个实现假设不存在名称冲突。例如,如果一个过滤器类型有Filter字段作为一个类型,而另一个类型有Filter字段作为不兼容的类型,这将不起作用,您必须使用其他方法。
当您在FilterMarshal中解组JSON时,您将得到一个与输入JSON匹配的对象树,然后,您可以构造实际的过滤器结构:

func unmarshalFilter(input *FilterMarshal) Filter {
   switch input.FilterType {
     case "and": 
        // process input.Filters recursively
       return AndFilter{Filters: processFilters(input.Filters))
     case "trait": 
       return TraitFilter{SomeKey: input.SomeKey}
   }
}
eh57zj3b

eh57zj3b2#

在没有明确的特性规范的情况下,您需要使用这个结构体来表示与您在问题中发布的JSON相近的内容。

type FilterContainer struct {
    Filter     map[string]interface{} `json:"Filter,omitempty"`
    Filters    []FilterContainer `json:"Filters,omitempty"`
    FilterType string `json:"FilterType"`
}

在Go语言中,过滤器的初始化过程如下

f := FilterContainer{
    Filters: []FilterContainer{
        {
            Filter:     map[string]interface{}{"someKey": "someValue1"},
            FilterType: "trait",
        },
        {
            Filter:     map[string]interface{}{"someKey": "someValue2"},
            FilterType: "trait",
        },
    },
    FilterType: "and",
}

当过滤器被封送为JSON并被漂亮地打印出来时,它们看起来像这样

{
  "Filters": [
    {
      "Filter": {
        "someKey": "someValue1"
      },
      "FilterType": "trait"
    },
    {
      "Filter": {
        "someKey": "someValue2"
      },
      "FilterType": "trait"
    }
  ],
  "FilterType": "and"
}

上面的JSON与您问题中的JSON不完全匹配;它缺少顶层的Filter字段,我认为我在这个答案中提出的结构体将简化实现和心理模型。
https://go.dev/play/p/6cLPT28DG2O的GoPlayground给予一下

如果您确实需要匹配问题中的JSON

下面是您需要的结构体

type FilterContainer struct {
    Filter     interface{} `json:"Filter,omitempty"` // Either a map[string]interface{} or a FilterContainer
    Filters    []FilterContainer `json:"Filters,omitempty"`
    FilterType string `json:"FilterType,omitempty"`
}

它是这样填充的

f := FilterContainer{
    Filter: FilterContainer{
        Filters: []FilterContainer{
            {
                Filter:     map[string]interface{}{"someKey": "someValue1"},
                FilterType: "trait",
            },
            {
                Filter:     map[string]interface{}{"someKey": "someValue2"},
                FilterType: "trait",
            },
        },
    },
    FilterType: "and",
}

https://go.dev/play/p/uB_LmJc6NGH的Go Playground中试用

相关问题