V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
b1anker
V2EX  ›  Go 编程语言

go 怎么读取一个 json 文件后对其字段进行删减?

  •  1
     
  •   b1anker · 2020-04-11 22:39:44 +08:00 · 3748 次点击
    这是一个创建于 1671 天前的主题,其中的信息可能已经有所发展或是发生改变。

    json 内容未知,要删除的字段也未知

    data.json:

    {
      "a": "hello",
      "b": 9527,
      "c": {
      	"d": 123
      }
    }
    

    假设构建出的程序名叫 app,

    希望 app --keys="a,b" --output="result.json"

    输出结果为 result.json:

    {
      "c": {
      	"d": 123
      }
    }
    

    刚学 go 没头绪。。。ioutil.ReadFile 读取文件,然后遍历 keys,用 jsoniter.Get 读取值?然后再拼成一个新的 json ?但是值是啥类型也不确定啊。。

    第 1 条附言  ·  2020-04-12 00:15:02 +08:00

    解决了,包名为extract

    输入data.json

    {
      "a": 2,
      "b": 3,
      "c": {
        "d": false
      }
    }
    

    main.go

    package main
    
    import (
    	"fmt"
    	"io/ioutil"
    	"os"
    	"strings"
    
    	jsoniter "github.com/json-iterator/go"
    )
    
    // Extract 提取JSON
    func Extract(extractType string, inputFileName string, keys []string) ([]byte, error) {
    
    	content, err := ioutil.ReadFile(inputFileName)
    
    	var inputJSON interface{}
    	var pickJSON map[string]interface{}
    
    	if err := jsoniter.Unmarshal(content, &inputJSON); err != nil {
    		panic(err)
    	}
    
    	if extractType == "pick" {
    		if err := jsoniter.Unmarshal([]byte("{}"), &pickJSON); err != nil {
    			panic(err)
    		}
    	}
    
    	if m, ok := inputJSON.(map[string]interface{}); ok {
    		for _, key := range keys {
    			if extractType == "omit" {
    				if _, exists := m[key]; exists {
    					delete(m, key)
    				}
    			} else {
    				pickJSON[key] = m[key]
    			}
    		}
    	}
    
    	var output []byte
    
    	if extractType == "omit" {
    		output, err = jsoniter.Marshal(inputJSON)
    	} else {
    		output, err = jsoniter.Marshal(pickJSON)
    	}
    
    	return output, err
    }
    
    func main() {
    	pluginsKeys := os.Getenv("PLUGIN_KEYS")
    	extractType := os.Getenv("PLUGIN_TYPE")
    	inputFileName := os.Getenv("PLUGIN_FILENAME")
    	outputFileName := os.Getenv("PLUGIN_OUTPUT")
    	keys := strings.Split(pluginsKeys, ",")
    
    	if extractType == "" {
    		extractType = "omit"
    	}
    
    	if outputFileName == "" {
    		outputFileName = "result.json"
    	}
    
    	output, err := Extract(extractType, inputFileName, keys)
    
    	if err != nil {
    		panic(err)
    	}
    
    	err = ioutil.WriteFile(outputFileName, output, 0777)
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(string(output))
    }
    

    测试,编译好后执行

    PLUGIN_TYPE=omit PLUGIN_FILENAME=data.json PLUGIN_KEYS=a,b extract
    

    输出

    {"c":{"d":false}}
    
    16 条回复    2020-04-13 13:34:42 +08:00
    KallyDev
        1
    KallyDev  
       2020-04-11 22:46:33 +08:00 via iPhone
    只写 c 和 d 的结构体,或者在 tag 中忽略 a 和 b

    可以看看这篇教程 https://colobu.com/2017/06/21/json-tricks-in-Go/
    loading
        2
    loading  
       2020-04-11 22:53:06 +08:00 via Android
    go 和 json 打交道基本就是结构体。
    Trim21
        3
    Trim21  
       2020-04-11 22:55:22 +08:00
    转成 map[string]interface{},然后删掉字段再转回去

    感觉用 go 做这个事情就是费力不讨好。。。
    cmdOptionKana
        4
    cmdOptionKana  
       2020-04-11 22:56:08 +08:00
    一般来说,类型应该可以确定的。如果真的不确定,可以用 interface{}
    current
        5
    current  
       2020-04-11 23:00:28 +08:00
    抛个砖

    package main

    import (
    "os"
    "encoding/json"
    "io/ioutil"
    "fmt"
    "strings"
    )

    var (
    filename string
    rm_fields []string
    )

    func parseArg() {
    if len(os.Args) != 3 {
    fmt.Println("Usage ./a.out $filename $rmFields")
    panic(0)
    }
    filename = os.Args[1]
    rm_fields = strings.Split(os.Args[2], ",")
    }

    func main() {
    parseArg()

    data, e := ioutil.ReadFile(filename)
    if e != nil {
    panic(e)
    }

    var tmp map[string]interface{}
    e = json.Unmarshal(data, &tmp)
    if e != nil {
    panic(e)
    }

    for _, field := range rm_fields {
    delete(tmp, field)
    }

    out, e := json.Marshal(tmp)
    if e != nil {
    panic(e)
    }
    fmt.Println(string(out))
    }
    feelinglucky
        6
    feelinglucky  
       2020-04-11 23:01:38 +08:00
    @Trim21 正解
    leoleoasd
        8
    leoleoasd  
       2020-04-11 23:37:34 +08:00
    或者用 json.Raw?
    index90
        9
    index90  
       2020-04-11 23:45:19 +08:00
    json.Unmarshal 成 map 啊,然后对 map 操作,完了再 json.Marshal 回去
    darrh00
        10
    darrh00  
       2020-04-11 23:55:27 +08:00
    一条命令的事: jq 'del(.a)|del(.b)' test.json

    另外 jq 的 golang 移植 https://github.com/itchyny/gojq
    darrh00
        11
    darrh00  
       2020-04-11 23:56:18 +08:00
    #10

    简单一点的 jq 'del(.a,.b)' test.json
    b1anker
        12
    b1anker  
    OP
       2020-04-12 00:07:48 +08:00
    @current 亲测可行!
    b1anker
        13
    b1anker  
    OP
       2020-04-12 00:09:05 +08:00
    @Trim21 恩,是这么操作了
    yxlimo
        14
    yxlimo  
       2020-04-12 07:18:28 +08:00 via iPhone
    不需要那么复杂,
    https://github.com/tidwall/jj
    这个库了解一下
    NullErro
        15
    NullErro  
       2020-04-13 08:33:45 +08:00
    python 处理这个不香吗
    guonaihong
        16
    guonaihong  
       2020-04-13 13:34:42 +08:00
    试下这个,也挺不错。https://github.com/tidwall/sjson

    ```go
    Delete a value:

    value, _ := sjson.Delete(`{"name":{"first":"Sara","last":"Anderson"}}`, "name.first")
    println(value)

    // Output:
    // {"name":{"last":"Anderson"}}
    ```
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5484 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 06:46 · PVG 14:46 · LAX 22:46 · JFK 01:46
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.