1. 程式人生 > 其它 >在golang中使用json區分空欄位和未設定欄位(轉載)

在golang中使用json區分空欄位和未設定欄位(轉載)

GO JSON解析空欄位的問題

來源:https://blog.csdn.net/weixin_26711425/article/details/108935566

A few weeks ago, I was working on a Golang microservice where I needed to add support for CRUD operations with JSON data. Normally, I would make a structure for the entity with all the fields defined in it along with ‘omitempty’ attribute as shown

幾周前,我正在研究Golang微服務,需要在其中新增對使用JSON資料進行CRUD操作的支援。 通常,我會為實體建立一個結構,其中定義了所有欄位以及'omitempty'屬性,如下所示

type Article struct {
   Id   string      `json:"id"`
   Name string      `json:"name,omitempty"`
   Desc string      `json:"desc,omitempty"`
}

Problem

問題

But this kind of representation poses a serious problem, especially for Update or Edit operations.

但是這種表示形式帶來了嚴重的問題,尤其是對於Update或Edit操作。

For example, let’s say that the update request JSON looks something like this

例如,假設更新請求JSON看起來像這樣

{"id":"1234","name":"xyz","desc":""}
Notice the empty desc field. Now let's see how it is unmarshalled in Go

注意空的desc欄位。 現在讓我們看看如何在Go中將其解組

func Test_JSON1(t *testing.T) {         
   jsonData:=`{"id":"1234","name":"xyz","desc":""}`
   req:=Article{}
   _=json.Unmarshal([]byte(jsonData),&req)
   fmt.Printf("%+v",req)
}Output:
=== RUN   Test_JSON1
{Id:1234 Name:xyz Desc:}

Here description comes as an empty string, It’s clearly visible that the client wants to set desc as an empty string and that is inferred by our program.

這裡的描述是一個空字串,很明顯,客戶端希望將desc設定為一個空字串,這是由我們的程式推斷出來的。

But what if the client does not want the change the existing value for Description, in that case, sending a big description string again is not the right thing to do, hence the JSON would look like this

但是,如果客戶端不希望更改Description的現有值,在那種情況下,再次傳送大的描述字串是不正確的事情,因此JSON看起來像這樣

{"id":"1234","name":"xyz"}
Let’s unmarshal it into our structure

讓我們將其解組到我們的結構中

func Test_JSON2(t *testing.T) {         
   jsonData:=`{"id":"1234","name":"xyz"}`
   req:=Article{}
   _=json.Unmarshal([]byte(jsonData),&req)
   fmt.Printf("%+v",req)
}Output:
=== RUN   Test_JSON2
{Id:1234 Name:xyz Desc:}

Well, we still get Desc as an empty string , so how do we differentiate between not-set field and empty field

好了,我們仍然將Desc作為空字串獲取,那麼如何區分未設定欄位和空欄位

Short answer? Pointers

簡短的答案? 指標

Solution

This is inspired by some existing Golang libraries like go-github. We can change our struct fields to pointer types, which would look like this

這受一些現有的Golang庫(例如go-github )的啟發。 我們可以將結構欄位更改為指標型別,如下所示

type Article struct {
   Id    string      `json:"id"`
   Name *string      `json:"name,omitempty"`
   Desc *string      `json:"desc,omitempty"`
}

By doing this we add an extra state to our fields. If the field does not exist in the raw JSON then the struct field will be null (nil).

通過這樣做,我們向欄位添加了額外的狀態。 如果原始JSON中不存在該欄位,則struct欄位將為null( nil )。

On the other hand, if the field does exist and its value is empty, then the pointer is not null and the field contains the empty value.

另一方面,如果該欄位確實存在並且其值為空,則指標不為null,並且該欄位包含空值。

Note- I did not change ‘Id’ to a pointer type because it cannot have a null state, id needs to be present at all times, its similar to a database id.

注意 -我沒有將' Id'更改為指標型別,因為它不能具有null狀態,id必須始終存在,類似於資料庫id。

Let’s try it out.

讓我們嘗試一下。

func Test_JSON_Empty(t *testing.T) {
   jsonData := `{"id":"1234","name":"xyz","desc":""}`
   req := Article{}
   _ = json.Unmarshal([]byte(jsonData), &req)
   fmt.Printf("%+v\n", req)
   fmt.Printf("%s\n", *req.Name)
   fmt.Printf("%s\n", *req.Desc)
}
func Test_JSON_Nil(t *testing.T) {
   jsonData := `{"id":"1234","name":"xyz"}`
   req := Article{}
   _ = json.Unmarshal([]byte(jsonData), &req)
   fmt.Printf("%+v\n", req)
   fmt.Printf("%s\n", *req.Name)
}
Output

輸出量

=== RUN   Test_JSON_Empty
{Id:1234 Name:0xc000088540 Desc:0xc000088550}
Name: xyz
Desc: 
--- PASS: Test_JSON_Empty (0.00s)=== RUN   Test_JSON_Nil
{Id:1234 Name:0xc00005c590 Desc:<nil>}
Name: xyz
--- PASS: Test_JSON_Nil (0.00s)

In the first case, as the description is set to an empty string, we get a non-null pointer in Desc with an empty string value. In the second case , where the field is not-set we get a null string pointer.

在第一種情況下,由於描述設定為空字串,因此在Desc中獲得了一個非空指標,該指標具有空字串值。 在第二種情況下,未設定欄位,我們得到一個空字串指標。

Hence we are able to differentiate between the two kinds of updates. This way works not just for strings but all the other data types including integers, nested structs, etc.

因此,我們能夠區分兩種更新。 這種方式不僅適用於字串,而且適用於所有其他資料型別,包括整數,巢狀結構等。

But this approach also comes with some problems.

但是這種方法也存在一些問題。

Null Safety: Non-pointer data types have inherent null safety. Meaning, a string or int can never be null in Golang. They always have a default value. But if pointers are defined then those data types are null by default if not set manually. Hence trying to access the data of those pointers without verifying the nullability can lead to crashes in your application.

空安全性:非指標資料型別具有固有的空安全性。 這意味著在Golang中,字串或int永遠不能為null。 它們始終具有預設值。 但是,如果定義了指標,那麼如果未手動設定,則這些資料型別預設為空。 因此,嘗試在不驗證可空性的情況下訪問那些指標的資料可能導致應用程式崩潰。

#The following code will crash because desc is null
func Test_JSON_Nil(t *testing.T) {
   jsonData := `{"id":"1234","name":"xyz"}`
   req := Article{}
   _ = json.Unmarshal([]byte(jsonData), &req)
   fmt.Printf("%+v\n", req)
   fmt.Printf("%s\n", *req.Desc)
}

This can be easily fixed by always checking for null pointers, but may make your code look dirty.

通過始終檢查空指標可以很容易地解決此問題,但可能會使您的程式碼看起來很髒。

Printability: As you might have noticed in the pointer-based solution output, the value of the pointers is not printed. Instead, the hex pointer is printed which is not very useful in applications. This can also be overcome by implementing the stringer interface.

可列印性:正如您在基於指標的解決方案輸出中可能已經注意到的那樣,不會列印指標的值。 而是列印十六進位制指標,這在應用程式中不是很有用。 這也可以通過實現縱梁介面來克服。

func (a *Article) String() string {
   output:=fmt.Sprintf("Id: %s ",a.Id)
   if a.Name!=nil{
   output+=fmt.Sprintf("Name: '%s' ",*a.Name)
   }
   if u.Desc!=nil{
   output+=fmt.Sprintf("Desc: '%s' ",u.Desc)
   }
   return output
}

Appendix:

附錄:

Another solution to the above problem can be using an external library to have nullable types which provide you methods to check if they are null or not without caring about pointers.
解決上述問題的另一種方法是使用外部庫具有可為null的型別,這些型別可為您提供檢查它們是否為null的方法,而無需關心指標。
https://github.com/guregu/null

https://github.com/guregu/null

https://github.com/google/go-github

https://github.com/google/go-github

翻譯自: https://medium.com/@arpitkh96/differentiate-between-empty-and-not-set-fields-with-json-in-golang-957bb2c5c065