【问题标题】:Unmarshal json field into struct field of type *regexp.Regexp将 json 字段解组为 *regexp.Regexp 类型的结构字段
【发布时间】:2020-10-14 17:26:01
【问题描述】:

我正在尝试将带有一些基于正则表达式的规则的 JSON 文件解组到我的结构中。

请看下面我的结构。

// GithubProjectMatcher matches a repository with a project
type GithubProjectMatcher struct {
    Rules map[string]GithubProjectMatcherRule `json:"rules,omitempty"`
}

// GithubProjectMatcherRule rule that matches a repository to a project
type GithubProjectMatcherRule struct {
    URL *regexp.Regexp `json:"url,omitempty"`
}

在这里查看我的 json

{
  "rules": {
    "Project One": { "url": "tabia|varys|garo" },
    "Project Two": { "url": "(?i)lem\\-" },
  }
}

如果我将这些正则表达式硬编码到它们正在工作的代码中。

例如

regexp.MustCompile("tabia|varys|garo")

必须做什么才能将这些解码到我的结构中?

我尝试如下解码。

f, err := os.Open("rules.json")
if err != nil {
   return err
}
defer f.Close()
err := json.NewDecoder(f).Decode(&m)
if err != nil {
   return err
}

【问题讨论】:

  • 你不能,不能直接。您需要一个将正则表达式类型作为字段的自定义类型,然后让自定义类型实现 json unmarshaler 接口。如果您希望能够直接通过自定义类型使用正则表达式方法,则可以嵌入该字段。
  • 或者你可以在GithubProjectMatcherRule上写一个自定义的UnmarshalJSON。无论哪种方式,您都希望将 JSON 中的 RE 存储为纯字符串,并在解组期间将 Compile 存储到 regexp.Regexp
  • 谢谢,我早该知道的,以前做过。将在此处分享结果作为答案以及未来的读者。
  • 包 encoding/json 文档的哪一部分暗示了这是可能的? *regexp.Regexp 在这个包可以处理的类型中吗?

标签: json regex go


【解决方案1】:

(Un) 编组正则表达式非常容易。它只需要创建一个嵌入regexp.Regexp的自定义类型:

import "regexp"

// MyRegexp embeds a regexp.Regexp, and adds Text/JSON
// (un)marshaling.
type MyRegexp struct {
    regexp.Regexp
}

// Compile wraps the result of the standard library's
// regexp.Compile, for easy (un)marshaling.
func Compile(expr string) (*MyRegexp, error) {
    re, err := regexp.Compile(expr)
    if err != nil {
        return nil, err
    }
    return &MyRegexp{*re}, nil
}

// UnmarshalText satisfies the encoding.TextMarshaler interface,
// also used by json.Unmarshal.
func (r *MyRegexp) UnmarshalText(text []byte) error {
    rr, err := Compile(string(text))
    if err != nil {
        return err
    }
    *r = *rr
    return nil
}

// MarshalText satisfies the encoding.TextMarshaler interface,
// also used by json.Marshal.
func (r *MyRegexp) MarshalText() ([]byte, error) {
    return []byte(r.String()), nil
}

【讨论】:

  • 我现在意识到您和我在同一时间发布了解决方案。很好,我们都非常接近我们的解决方案
【解决方案2】:

解决方法如下:

通过实现 TextMarshaler 和 TextUnmarshaler 接口,您可以定义值应如何编组或取消编组。

https://golang.org/pkg/encoding/#TextMarshaler https://golang.org/pkg/encoding/#TextUnmarshaler

// GithubMetadataFactory allows to provide a custom generated metadata
type GithubMetadataFactory func(repo github.Repository) Metadata

// GithubProjectMatcher matches a repository with a project
type GithubProjectMatcher struct {
    Rules map[string]GithubProjectMatcherRule `json:"rules,omitempty"`
}

// GithubProjectMatcherRule rule that matches a repository to a project
type GithubProjectMatcherRule struct {
    URL *Regexp `json:"url,omitempty"`
}


// Regexp adds unmarshalling from json for regexp.Regexp
type Regexp struct {
    *regexp.Regexp
}

// UnmarshalText unmarshals json into a regexp.Regexp
func (r *Regexp) UnmarshalText(b []byte) error {
    regex, err := regexp.Compile(string(b))
    if err != nil {
        return err
    }

    r.Regexp = regex

    return nil
}

// MarshalText marshals regexp.Regexp as string
func (r *Regexp) MarshalText() ([]byte, error) {
    if r.Regexp != nil {
        return []byte(r.Regexp.String()), nil
    }

    return nil, nil
}

请参阅此处,了解 Go Playground 中的完整工作示例。 https://play.golang.org/p/IS60HuuamLM

对于更复杂的数据类型,您还可以在您的类型上实现 json Marshaler 和 Unmarshaler 接口。

https://golang.org/pkg/encoding/json/#Marshaler https://golang.org/pkg/encoding/json/#Unmarshaler

这两个例子也可以在这里找到。

https://golang.org/pkg/encoding/json/

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-03-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-10
    • 2017-10-07
    • 1970-01-01
    相关资源
    最近更新 更多