2 Go语言JSON与XML解析与表单操作

在这里插入图片描述

1 数据交互的格式

常见的数据交互格式有

  • JSON:JavaScript Object Notation,轻量级的数据交换格式,如:{"name":"lisi","address": ["广州","深圳"]}
  • XML:工业开发中常用的数据交互标准格式

2 JSON方式

2.1 JSON序列化

JSON序列化与反序列化需要使用encoding/json包,如下案例所示:

type Person struct {
	Name string
	Age int
}

p := Person {
	Name: "lisi",
	Age: 50,
}

data, _ := json.Marshal(&p)
fmt.Printf(string(data));	 	//{"Name":"lisi","Age":50}

同理,我们也可以使用上述方法对基本数据类型、切片、map等数据进行序列化。

在结构体序列化时,如果希望序列化后的key的名字可以自定义,可以给该结构体指定一个tag标签:

type Person struct {
	Name string `json:"my_name"`
	Age int `json:"my_age"`
}
	//序列化的结果:{"my_name":"lisi","my_age":50}

在定义struct tag的时候需要注意的几点是:

  • 字段的tag是"-",那么这个字段不会输出到JSON
  • tag中如果带有"omitempty"选项,那么如果该字段值为空,就不会输出到JSON串中
  • 如果字段类型是bool, string, int, int64等,而tag中带有",string"选项,那么这个字段在输出到JSON的时候会把该字段对应的值转换成JSON字符串
  • JSON对象只支持string作为key,所以要编码一个map,那么必须是map[string]T这种类型(T是Go语言中任意的类型)
  • Channel, complex和function是不能被编码成JSON的
  • 嵌套的数据是不能编码的,不然会让JSON编码进入死循环
  • 指针在编码的时候会输出指针指向的内容,而空指针会输出null

2.2 JSON反序列化

str :=  `{"Name":"lisi","Age":50}`

// 反序列化json为结构体
type Person struct {
	Name string 
	Age int 
}

var p Person
json.Unmarshal([]byte(str), &p)
fmt.Println(p)							//{lisi 50}

2.3 解析到interface

2.1和2.2的案例中,我们知道json的数据结构,可以直接进行序列化操作,如果不知道JSON具体的结构,就需要解析到interface,因为interface{}可以用来存储任意数据类型的对象。

JSON包中采用map[string]interface{}[]interface{}结构来存储任意的JSON对象和数组。Go类型和JSON类型的对应关系如下:

  • bool 代表 JSON booleans,
  • float64 代表 JSON numbers,
  • string 代表 JSON strings,
  • nil 代表 JSON null

现在我们假设有如下的JSON数据

jsonStr := `{"Name":"Lisi","Age":6,"Parents":["Lisan","WW"]}`
jsonBytes := []byte(jsonStr)

var i interface{}
json.Unmarshal(jsonBytes, &i)
fmt.Println(i)		// map[Age:6 Name:Lisi Parents:[Lisan WW]]

上述变量i存储了存储了一个map类型,key是strig,值存储在空接口内,
如果在我们不知道他的结构的情况下,我们把他解析到interface{}里面,其真实结构如下:

i = map[string]interface{}{
	"Name": "Lisi",
	"Age":  6,
	"Parents": []interface{}{
		"Lisan",
		"WW",
	},
}

由于是空接口类型,无法直接访问,需要使用断言方式:

m := i.(map[string]interface{})
for k, v := range m {
	switch r := v.(type) {
	case string:
		fmt.Println(k, " is string ", r)
	case int:
		fmt.Println(k, " is int ", r)
	case []interface{}:
		fmt.Println(k, " is array ", )
		for i, u := range r {
			fmt.Println(i, u)
		}
	default:
		fmt.Println(k, " cannot be recognized")
	}
}

上面是官方提供的解决方案,操作起来不是很方便,推荐使用第三方包有:

3 XML方式

3.1 解析XML

现在有如下books.xml示例:

<?xml version="1.0" encoding="utf-8"?>
<books version="1">
	<book>
		<bookName>离散数学</bookName>
		<bookPrice>120</bookPrice>
	</book>
	<book>
		<bookName>人月神话</bookName>
		<bookPrice>75</bookPrice>
	</book>
</books>

通过xml包的Unmarshal函数来解析:

package main

import (
	"encoding/xml"
	"fmt"
	"io/ioutil"
	"os"
)

type BookStore struct {
	XMLName     xml.Name `xml:"books"`
	Version     string   `xml:"version,attr"`
	Store       []book	 `xml:"book"`
	Description string   `xml:",innerxml"`
}

type book struct {
	XMLName    	xml.Name `xml:"book"`
	BookName 	string   `xml:"bookName"`
	BookPrice   string   `xml:"bookPrice"`
}

func main() {

	file, err := os.Open("books.xml") 		
	if err != nil {
		fmt.Printf("error: %v", err)
		return
	}
	defer file.Close()
	data, err := ioutil.ReadAll(file)
	if err != nil {
		fmt.Printf("error: %v", err)
		return
	}

	v := BookStore{}
	err = xml.Unmarshal(data, &v)
	if err != nil {
		fmt.Printf("error: %v", err)
		return
	}

	fmt.Println(v)
}

3.2 生成XML

xml包中的MarshalMarshalIndent两个函数,可以用来生成xml。这两个函数主要的区别是第二个函数会增加前缀和缩进,函数的定义如下所示:

package main

import (
	"encoding/xml"
	"fmt"
	"os"
)

type BookStore struct {
	XMLName     xml.Name `xml:"books"`
	Version     string   `xml:"version,attr"`
	Store       []book	 `xml:"book"`
}

type book struct {
	BookName 	string   `xml:"bookName"`
	BookPrice   string   `xml:"bookPrice"`
}

func main() {

	bs := &BookStore{Version: "1"}
	bs.Store = append(bs.Store, book{"离散数学", "120"})
	bs.Store = append(bs.Store, book{"人月神话", "75"})

	output, err := xml.MarshalIndent(bs, "  ", "    ")
	if err != nil {
		fmt.Printf("error: %v\n", err)
	}

	// 生成正确xml头
	os.Stdout.Write([]byte(xml.Header))
	os.Stdout.Write(output)
}

4 字段校验

通过内置函数len()可以获取字符串的长度,以此可以校验参数的合法性:

if len(r.Form["username"][0])==0{
	//为空的处理
}

r.Form对不同类型的表单元素的留空有不同的处理:

  • 空文本框、空文本区域以及文件上传,元素的值为空值
  • 未选中的复选框和单选按钮,则不会在r.Form中产生相应条目,如果我们用上面例子中的方式去获取数据时程序就会报错。所以我们需要通过r.Form.Get()来获取值,因为如果字段不存在,通过该方式获取的是空值。但是通过r.Form.Get()只能获取单个的值,

5 文件上传

2.1 前后端模拟上传

前端代码:

<html>
<head>
	<title>上传文件</title>
</head>
<body>
<form enctype="multipart/form-data" action="/upload" method="post">
  <input type="file" name="uploadfile" />
  <input type="submit" value="upload" />
</form>
</body>
</html>

form的enctype属性有如下三种情况:

application/x-www-form-urlencoded		# 表示在发送前编码所有字符(默认)
multipart/form-data	  					# 文件上传使用,不会不对字符编码。
text/plain	  							# 空格转换为 "+" 加号,但不对特殊字符编码。

golang的后端处理代码:

// 上传文件处理路由:http.HandleFunc("/upload", upload)
func upload(w http.ResponseWriter, r *http.Request) {

	// 设置上传文件能使用的内存大小,超过了,则存储在系统临时文件中
	r.ParseMultipartForm(32 << 20)
	// 获取上传文件句柄
	file, handler, err := r.FormFile("uploadfile")
	if err != nil {
		fmt.Println(err)
		return
	}
	defer file.Close()

	fmt.Fprintf(w, "%v", handler.Header)
	f, err := os.OpenFile("./upload/" + handler.Filename, os.O_WRONLY|os.O_CREATE, 0666)
	if err != nil {
		fmt.Println(err)
		return
	}
	defer f.Close()
	io.Copy(f, file)
}

2.2 go客户端模拟上传

Go支持模拟客户端表单功能支持文件上传:

package main

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"os"
)

func postFile(filename string, targetUrl string) error {
	bodyBuf := &bytes.Buffer{}
	bodyWriter := multipart.NewWriter(bodyBuf)

	//关键的一步操作
	fileWriter, err := bodyWriter.CreateFormFile("uploadfile", filename)
	if err != nil {
		fmt.Println("error writing to buffer")
		return err
	}

	//打开文件句柄操作
	fh, err := os.Open(filename)
	if err != nil {
		fmt.Println("error opening file")
		return err
	}
	defer fh.Close()
	
	//iocopy
	_, err = io.Copy(fileWriter, fh)
	if err != nil {
		return err
	}

	contentType := bodyWriter.FormDataContentType()
	bodyWriter.Close()

	resp, err := http.Post(targetUrl, contentType, bodyBuf)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	resp_body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	fmt.Println(resp.Status)
	fmt.Println(string(resp_body))
	return nil
}

// sample usage
func main() {
	target_url := "http://localhost:8080/upload"
	filename := "./test.pdf"
	postFile(filename, target_url)
}

3 防止重复提交

防止表单重复提交的方案有很多,其中之一是在表单中添加一个带有唯一值的隐藏字段:

  • 1.在服务器端生成一个唯一的随机标识号,专业术语称为Token(令牌),同时在当前用户的Session域中保存这个Token。
  • 2.将Token发送到客户端的Form表单中,在Form表单中使用隐藏域来存储这个Token
  • 3.表单提交的时候连同这个Token一起提交到服务器端,然后在服务器端判断客户端提交上来的Token与服务器端生成的Token是否一致,如果不一致,那就是重复提交了,此时服务器端就可以不处理重复提交的表单。如果相同则处理表单提交,处理完后清除当前用户的Session域中存储的标识号。

在下列情况下,服务器程序将拒绝处理用户提交的表单请求:

  • 存储Session域中的Token(令牌)与表单提交的Token(令牌)不同。
  • 当前用户的Session中不存在Token(令牌)。
  • 用户提交的表单数据中没有Token(令牌)。
用户名:<input type="text" name="username">
密码:<input type="password" name="password">
<input type="hidden" name="token" value="{{.}}">
<input type="submit" value="登陆">

在模版里面增加了一个隐藏字段token,该值通过MD5(时间戳)来确定唯一值,然后我们把这个值存储到服务器端,以方便表单提交时比对判定。

func login(w http.ResponseWriter, r *http.Request) {

	r.ParseForm()
	token := r.Form.Get("token")
    
    if token != "" {
		//验证token的合法性
	} else {
		//不存在token报错
    }
    
    // 执行具体登录业务
}

在这里插入图片描述

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
### 回答1: Python数据解析JSONXML是指使用Python编程语言解析JSONXML格式的数据。其中JSON是一种轻量级的数据交换格式,常用于Web应用程序中的数据传递;XML是一种标记语言,可用于数据存储和传输。使用Python的内置库或第三方库,可以对JSONXML数据进行解析和处理。 ### 回答2: Python是一种流行的脚本语言,它被广泛用于数据解析和处理,包括解析jsonxml格式的数据。JSON是JavaScript对象表示法,一个轻量级的数据交换格式。XML是可扩展标记语言,也是一种常用的数据交换格式。在Python中,解析jsonxml数据的方法略有不同。 1. 解析JSON数据 Python内置了一个json模块,可以轻松地解析JSON数据。该模块包含两个主要函数,load()和loads()。load()函数将JSON数据从文件读取并解析为Python对象,而loads()函数则将JSON字符串解析为Python对象。例如,以下是解析JSON数据的样例代码: import json # 从文件中加载JSON数据 with open('data.json') as f: data = json.load(f) # 解析JSON字符串 json_str = '{"name": "John", "age": 30, "city": "New York"}' data = json.loads(json_str) 在上面的代码中,我们使用了json.load()函数从文件中加载JSON数据,并使用json.loads()函数将JSON字符串解析为Python对象。 2. 解析XML数据 Python中的XML解析器包括xml.dom和xml.sax。使用xml.dom可以将整个XML文档解析为一个DOM对象树,而使用xml.sax则将解析器分离为一个解析器、一个文档和一个事件处理程序。以下是使用xml.dom解析XML数据的示例代码: import xml.dom.minidom # 从文件中加载XML数据 dom = xml.dom.minidom.parse('data.xml') # 获取根元素对象 root = dom.documentElement # 获取所有子元素 items = root.getElementsByTagName('item') # 遍历子元素 for item in items: id = item.getAttribute('id') name = item.getElementsByTagName('name')[0].childNodes[0].nodeValue desc = item.getElementsByTagName('description')[0].childNodes[0].nodeValue price = item.getElementsByTagName('price')[0].childNodes[0].nodeValue print(id, name, desc, price) 在上面的代码中,我们使用xml.dom.minidom模块解析XML数据,可以使用document对象获取根元素对象,然后使用getElementsByTagName()方法获取所有具有相同名称的元素,从而遍历XML数据并提取所需的数据。 总之,Python提供了许多工具和库,可帮助您轻松地解析JSONXML格式的数据。根据您的需求,可以选择使用jsonxml解析器来处理数据。 无论您选择哪个解析器,都可以使用Python来轻松地解析和处理数据。 ### 回答3: Python是一种流行的编程语言,适用于各种用途,包括数据解析。在Python中,JSONXML是两种最常用的数据交换格式。虽然这两种格式都用于将数据从一个应用程序传递到另一个应用程序,但它们具有不同的语法。 JSON(JavaScript Object Notation)是一种轻量级数据格式,易于阅读和编写,在Web开发中尤其受欢迎。它使用键值对来表示数据,值可以是数字、字符串、布尔值、列表、对象或null。在Python中,可以使用json模块来解析JSON数据。json模块提供了许多函数来读取和写入JSON数据,包括loads()和dumps()函数。loads()函数允许将JSON字符串换为Python对象,而dumps()函数允许将Python对象换为JSON字符串。 XML(扩展标记语言)是一种用于传输和存储数据的标记语言,在Web服务、B2B通信和数据库管理等领域中广泛使用。与JSON不同,XML使用标签来表示数据。标签包含元素的名称和值,通常嵌套在其他元素中。在Python中,可以使用xml.etree.ElementTree模块来解析XML数据。xml.etree.ElementTree模块提供了ElementTree类,允许创建XML树,还有一些有用的函数,例如find()、iter()和parse(),用于搜索和解析XML树。 无论您使用JSON还是XML,Python都提供了功能强大的工具来解析和处理数据。选择哪种格式取决于您的应用程序的需求和数据的性质。如果数据是简单的键值对,JSON可能是一个更好的选择,而如果数据是复杂的、嵌套式结构,或者需要包含元数据,XML可能更适合。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

行走的皮卡丘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值