You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Go语言将非标准格式字符串转换为合法JSON结构?

解决非标准类JSON格式的解析问题(Go语言)

方案一:预处理为合法JSON后解析

这种方法适合格式相对固定的场景,先把非标准格式转换成合法JSON,再用Go标准库的encoding/json解析,新手更容易上手。

关键转换步骤

  • 给所有键名(如timestamp、content、elem等)添加双引号
  • 给字符串类型的值(如demoproj、default等)添加双引号,数字值保留原样
  • 将重复的elem字段合并为JSON数组

Go示例代码

package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"regexp"
)

// 定义对应的数据结构体
type Key struct {
	Key   string `json:"key"`
	Value string `json:"value"`
}

type Elem struct {
	Name string `json:"name"`
	Key  *Key   `json:"key,omitempty"` // 字段为空时不参与序列化
}

type Content struct {
	Origin string `json:"origin"`
	Elem   []Elem `json:"elem"`
}

type MyData struct {
	Timestamp int64   `json:"timestamp"`
	Content   Content `json:"content"`
}

type Root struct {
	MyData MyData `json:"mydata"`
}

func main() {
	// 1. 读取目标文件
	data, err := ioutil.ReadFile("your_data.txt") // 替换为你的文件路径
	if err != nil {
		fmt.Printf("读取文件失败: %v\n", err)
		return
	}
	rawStr := string(data)

	// 2. 预处理转换为合法JSON
	// 给键名添加双引号
	reKey := regexp.MustCompile(`(\w+):`)
	rawStr = reKey.ReplaceAllString(rawStr, `"$1":`)

	// 给字符串值添加双引号(跳过数字)
	reStrValue := regexp.MustCompile(`: (\w+)(?=\s|})`)
	rawStr = reStrValue.ReplaceAllStringFunc(rawStr, func(s string) string {
		if regexp.MustCompile(`: \d+`).MatchString(s) {
			return s
		}
		return regexp.MustCompile(`: (\w+)`).ReplaceAllString(s, ": \"$1\"")
	})

	// 将重复elem字段转为数组
	rawStr = regexp.MustCompile(`"elem": {`).ReplaceAllString(rawStr, `"elem": [`, 1)
	rawStr = regexp.MustCompile(`}, "elem": {`).ReplaceAllString(rawStr, "}, {")
	rawStr = regexp.MustCompile(`}\s*}`).ReplaceAllString(rawStr, "]}")

	// 处理根节点为合法JSON结构
	rawStr = regexp.MustCompile(`mydata ({.*})`).ReplaceAllString(rawStr, `"mydata": $1`)
	rawStr = fmt.Sprintf("{%s}", rawStr)

	// 3. 解析JSON到结构体
	var root Root
	err = json.Unmarshal([]byte(rawStr), &root)
	if err != nil {
		fmt.Printf("解析JSON失败: %v\n", err)
		fmt.Println("预处理后的JSON:", rawStr)
		return
	}

	// 打印解析结果
	fmt.Printf("解析成功:\n%+v\n", root)
}

方案二:自定义递归下降解析器

如果格式变动较大,预处理容易出错,可以手写解析器,通过逐词扫描直接处理结构。

核心思路

  1. 使用bufio.Scanner读取并分词(识别大括号、冒号、标识符、数字等)
  2. 递归解析嵌套结构,直接填充到目标结构体中

Go示例代码(简化版)

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
)

type Key struct {
	Key   string
	Value string
}

type Elem struct {
	Name string
	Key  *Key
}

type Content struct {
	Origin string
	Elem   []Elem
}

type MyData struct {
	Timestamp int64
	Content   Content
}

// 分词器
type tokenizer struct {
	scanner *bufio.Scanner
	current string
}

func newTokenizer(file *os.File) *tokenizer {
	scanner := bufio.NewScanner(file)
	scanner.Split(bufio.ScanWords)
	t := &tokenizer{scanner: scanner}
	t.next()
	return t
}

func (t *tokenizer) next() {
	if t.scanner.Scan() {
		t.current = t.scanner.Text()
	} else {
		t.current = ""
	}
}

func (t *tokenizer) expect(s string) error {
	if t.current != s {
		return fmt.Errorf("期望'%s',实际得到'%s'", s, t.current)
	}
	t.next()
	return nil
}

// 解析Key结构
func parseKey(t *tokenizer) (*Key, error) {
	key := &Key{}
	if err := t.expect("key:"); err != nil {
		return nil, err
	}
	key.Key = t.current
	t.next()
	if err := t.expect("value:"); err != nil {
		return nil, err
	}
	key.Value = t.current
	t.next()
	return key, nil
}

// 解析Elem结构
func parseElem(t *tokenizer) (*Elem, error) {
	elem := &Elem{}
	if err := t.expect("name:"); err != nil {
		return nil, err
	}
	elem.Name = t.current
	t.next()
	if t.current == "key" {
		t.next()
		if err := t.expect("{"); err != nil {
			return nil, err
		}
		k, err := parseKey(t)
		if err != nil {
			return nil, err
		}
		elem.Key = k
		if err := t.expect("}"); err != nil {
			return nil, err
		}
	}
	return elem, nil
}

// 解析Content结构
func parseContent(t *tokenizer) (*Content, error) {
	content := &Content{}
	if err := t.expect("origin:"); err != nil {
		return nil, err
	}
	content.Origin = t.current
	t.next()
	for t.current == "elem" {
		t.next()
		if err := t.expect("{"); err != nil {
			return nil, err
		}
		elem, err := parseElem(t)
		if err != nil {
			return nil, err
		}
		content.Elem = append(content.Elem, *elem)
		if err := t.expect("}"); err != nil {
			return nil, err
		}
	}
	return content, nil
}

// 解析MyData结构
func parseMyData(t *tokenizer) (*MyData, error) {
	mydata := &MyData{}
	if err := t.expect("timestamp:"); err != nil {
		return nil, err
	}
	ts, err := strconv.ParseInt(t.current, 10, 64)
	if err != nil {
		return nil, fmt.Errorf("解析timestamp失败: %v", err)
	}
	mydata.Timestamp = ts
	t.next()
	if err := t.expect("content"); err != nil {
		return nil, err
	}
	t.next()
	if err := t.expect("{"); err != nil {
		return nil, err
	}
	content, err := parseContent(t)
	if err != nil {
		return nil, err
	}
	mydata.Content = *content
	if err := t.expect("}"); err != nil {
		return nil, err
	}
	return mydata, nil
}

func main() {
	file, err := os.Open("your_data.txt")
	if err != nil {
		fmt.Printf("打开文件失败: %v\n", err)
		return
	}
	defer file.Close()

	t := newTokenizer(file)
	if t.current != "mydata" {
		fmt.Println("无效格式:根节点不是mydata")
		return
	}
	t.next()
	if err := t.expect("{"); err != nil {
		fmt.Printf("解析失败: %v\n", err)
		return
	}
	mydata, err := parseMyData(t)
	if err != nil {
		fmt.Printf("解析失败: %v\n", err)
		return
	}
	if err := t.expect("}"); err != nil {
		fmt.Printf("解析失败: %v\n", err)
		return
	}

	fmt.Printf("解析成功:\n%+v\n", mydata)
}

内容的提问来源于stack exchange,提问作者Developer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 09:30:40