zoukankan      html  css  js  c++  java
  • Golang mapstructure

    mapstructure

    转载:https://darjun.github.io/2020/07/29/godailylib/mapstructure/

    作用:

    用于将通用的map[string]interface{}解码到对应的 Go 结构体中,或者执行相反的操作。很多时候,解析来自多种源头的数据流时,我们一般事先并不知道他们对应的具体类型。只有读取到一些字段之后才能做出判断。这时,我们可以先使用标准的encoding/json库将数据解码为map[string]interface{}类型,然后根据标识字段利用mapstructure库转为相应的 Go 结构体以便使用。

    快速构建:

    $ mkdir mapstructure && cd mapstructure
    
    $ go mod init github.com/sixinshuier/mapstructure
    

    下载mapstructure库:

    $ go get github.com/mitchellh/mapstructure
    
    package main
    
    import (
    	"encoding/json"
    	"fmt"
    	"log"
    
    	"github.com/mitchellh/mapstructure"
    )
    
    type Person struct {
    	Name string
    	Age  int
    	Job  string
    }
    
    type Cat struct {
    	Name  string
    	Age   int
    	Breed string
    }
    
    func main() {
    	datas := []string{`
        { 
          "type": "person",
          "name":"dj",
          "age":18,
          "job": "programmer"
        }
      `,
    		`
        {
          "type": "cat",
          "name": "kitty",
          "age": 1,
          "breed": "Ragdoll"
        }
      `,
    	}
    
    	for _, data := range datas {
    		var m map[string]interface{}
    		err := json.Unmarshal([]byte(data), &m)
    		if err != nil {
    			log.Fatal(err)
    		}
    		switch m["type"].(string) {
    		case "person":
    			var p Person
    			mapstructure.Decode(m, &p)
    			fmt.Println("person", p)
    
    		case "cat":
    			var cat Cat
    			mapstructure.Decode(m, &cat)
    			fmt.Println("cat", cat)
    		}
    	}
    }
    
    

    字段标签 mapstructure:"username"

    type Person struct {
      Name string `mapstructure:"username"`
    }
    

    内嵌结构:mapstructure:",squash"

    type Friend struct {
      Person `mapstructure:",squash"`  // 将person的字段提到 friend上,即将结构体的字段提到父结构上
    }
    

    未映射的值:mapstructure:",remain"

    如果源数据中有未映射的值(即结构体中无对应的字段),mapstructure默认会忽略它。

    我们可以在结构体中定义一个字段,为其设置mapstructure:",remain"标签。这样未映射的值就会添加到这个字段中。注意,这个字段的类型只能为map[string]interface{}map[interface{}]interface{}。```

    package main
    
    import (
       "encoding/json"
       "fmt"
       "github.com/mitchellh/mapstructure"
       "log"
    )
    
    type Person struct {
       Name  string
       Age   int
       Job   string
       Other map[string]interface{} `mapstructure:",remain"`
    }
    
    func main() {
       data := `
        { 
          "name": "dj",
          "age":18,
          "job":"programmer",
          "height":"1.8m",
          "handsome": true
        }
      `
    
       var m map[string]interface{}
       err := json.Unmarshal([]byte(data), &m)
       if err != nil {
          log.Fatal(err)
       }
    
       var p Person
       mapstructure.Decode(m, &p)
       fmt.Println("other", p.Other)
    }
    

    逆向反转:mapstructure:",omitempty"

    前面我们都是将map[string]interface{}解码到 Go 结构体中。mapstructure当然也可以将 Go 结构体反向解码为map[string]interface{}。在反向解码时,我们可以为某些字段设置mapstructure:",omitempty"。这样当这些字段为默认值时,就不会出现在结构的map[string]interface{}中:

    type Person struct {
      Name string
      Age  int
      Job  string `mapstructure:",omitempty"`
    }
    
    func main() {
      p := &Person{
        Name: "dj",
        Age:  18,
      }
    
      var m map[string]interface{}
      mapstructure.Decode(p, &m)
    
      data, _ := json.Marshal(m)
      fmt.Println(string(data))
    }
    

    Metadata

    解码时会产生一些有用的信息,mapstructure可以使用Metadata收集这些信息。Metadata结构如下:

    // mapstructure.go
    type Metadata struct {
      Keys   []string
      Unused []string
    }
    

    Metadata只有两个导出字段:

    • Keys:解码成功的键名;
    • Unused:在源数据中存在,但是目标结构中不存在的键名。

    为了收集这些数据,我们需要使用DecodeMetadata来代替Decode方法:

    type Person struct {
      Name string
      Age  int
    }
    
    func main() {
      m := map[string]interface{}{
        "name": "dj",
        "age":  18,
        "job":  "programmer",
      }
    
      var p Person
      var metadata mapstructure.Metadata
      mapstructure.DecodeMetadata(m, &p, &metadata)
    
      fmt.Printf("keys:%#v unused:%#v\n", metadata.Keys, metadata.Unused)
    }
    

    返回结果:

    keys:[]string{"Name", "Age"} unused:[]string{"job"}
    

    弱类型输入:

    有时候,我们并不想对结构体字段类型和map[string]interface{}的对应键值做强类型一致的校验。这时可以使用WeakDecode/WeakDecodeMetadata方法,它们会尝试做类型转换:

    type Person struct {
      Name   string
      Age    int
      Emails []string
    }
    
    func main() {
      m := map[string]interface{}{
        "name":   123,
        "age":    "18",
        "emails": []int{1, 2, 3},
      }
    
      var p Person
      err := mapstructure.WeakDecode(m, &p)
      if err == nil {
        fmt.Println("person:", p)
      } else {
        fmt.Println(err.Error())
      }
    }
    
    

    解码器

    除了上面介绍的方法外,mapstructure还提供了更灵活的解码器(Decoder)。可以通过配置DecoderConfig实现上面介绍的任何功能:

    // mapstructure.go
    type DecoderConfig struct {
    	ErrorUnused       bool
    	ZeroFields        bool
    	WeaklyTypedInput  bool
    	Metadata          *Metadata
    	Result            interface{}
    	TagName           string
    }
    

    各个字段含义如下:

    • ErrorUnused:为true时,如果输入中的键值没有与之对应的字段就返回错误;
    • ZeroFields:为true时,在Decode前清空目标map。为false时,则执行的是map的合并。用在structmap的转换中;
    • WeaklyTypedInput:实现WeakDecode/WeakDecodeMetadata的功能;
    • Metadata:不为nil时,收集Metadata数据;
    • Result:为结果对象,在mapstruct的转换中,Resultstruct类型。在structmap的转换中,Resultmap类型;
    • TagName:默认使用mapstructure作为结构体的标签名,可以通过该字段设置。
    不要小瞧女程序员
  • 相关阅读:
    Redis 设置密码登录
    SELinux 宽容模式(permissive) 强制模式(enforcing) 关闭(disabled) 几种模式之间的转换...
    laravel 博客项目部署到Linux系统后报错 权限都设置为777,仍然报错没有权限
    linux用netstat查看服务及监听端口
    redis使用rediscli查看所有的keys及清空所有的数据
    一起谈.NET技术,Oxite 项目结构分析 狼人:
    一起谈.NET技术,VS 2010 和 .NET 4.0 系列之《在VS 2010中查询和导航代码》篇 狼人:
    一起谈.NET技术,VS 2010 和 .NET 4.0 系列之《添加引用对话框的改进》篇 狼人:
    一起谈.NET技术,VS 2010 和 .NET 4.0 系列之《代码优化的Web开发Profile》篇 狼人:
    一起谈.NET技术,数组排序方法的性能比较(3):LINQ排序实现分析 狼人:
  • 原文地址:https://www.cnblogs.com/shix0909/p/15655432.html
Copyright © 2011-2022 走看看