21

Is there a way to serialize an empty array attribute (not null) of a struct and deserialize it back to an empty array (not null again)?

Considering that an empty array is actually a pointer to null, is the perceptible initial difference between an empty array and pointer to null completely lost after serialize/deserialize?

The worst practical scenario is that when I show an empty array attribute to my REST client, as a json "att":[], at first time, and, after cache register to redis and recover it, the same attribute is shown to my client as "att":null, causing a contract broken and a lot of confusing.

Summing up: is possible to show the Customer 2 addresses like an json empty array, after serialize/deserialize => https://play.golang.org/p/TVwvTWDyHZ

DLopes
  • 567
  • 2
  • 6
  • 13
  • 3
    In Go, [arrays and **slices** are very different things](https://blog.golang.org/slices). You almost certainly mean the later. – Dave C Oct 17 '15 at 06:31

3 Answers3

18

I am pretty sure the easiest way you can do it is to change your line

var cust1_recovered Customer

to

cust1_recovered := Customer{Addresses: []Address{}}

Unless I am reading your question incorrectly, I believe this is your desired output:

ORIGINAL  Customer 2 {
  "Name": "Customer number 2",
  "Addresses": []
}
RECOVERED Customer 2 {
  "Name": "Customer number 2",
  "Addresses": []
}

Here is a playground to verify with: https://play.golang.org/p/T9K1VSTAM0

The limitation here, as @mike pointed out, is if Addresses is truly nil before you encode, once you decode you do not get the json equivalent null, but would instead end up with an empty list.

sberry
  • 128,281
  • 18
  • 138
  • 165
  • Not sure why this was down-voted. It does what the OP is asking for, returning an empty json array in place of a null. – sberry Oct 17 '15 at 05:42
  • 1
    Not the downvoter, but I would assume because it doesn't actually differentiate the two. If it was originally `nil`, it would now be `[]`. – Mike Precup Oct 17 '15 at 05:45
  • 2
    @MikePrecup: Unless I am reading the OP's question incorrectly, I believe that is what they are after. – sberry Oct 17 '15 at 05:48
  • Arf, my bad, I misunderstood your answer and downvoted too quickly, sorry. It's locked now, but if you can edit your answer (adding a playground link for example) I'll be able to change this into an upvote. Edit: ok, upvoted – HectorJ Oct 17 '15 at 05:49
  • @MikePrecup, I see your point. I guess that is up to the OP and perhaps the system in question as to whether nil Addresses are possible. Based on the comment about "causing a contract broken" I would assume the empty array is always desired. – sberry Oct 17 '15 at 05:53
  • Good point, depends on what the contract is! I'll leave my answer for posterity in case anyone else has the other problem. – Mike Precup Oct 17 '15 at 17:44
13

No, it's not possible. To understand why, let's look at the Go spec. For it to output two different results for empty vs. nil, any serialization method would need to be able to tell the difference between the two. However, according to the Go spec,

Two array types are identical if they have identical element types and the same array length.

Since neither contains any elements and have the same element type, the only difference could be in length, but it also states that

The length of a nil slice, map or channel is 0

So through comparison, it would be unable to tell. Of course, there are methods other than comparison, so to really put the nail in the coffin, here's the portion that shows they have the same underlying representation. The spec also guarantees that

A struct or array type has size zero if it contains no fields (or elements, respectively) that have a size greater than zero.

so the actual allocated structure of a zero length array has to be of size zero. If it's of size zero, it can't store any information about whether it's empty or nil, so the object itself can't know either. In short, there is no difference between a nil array and a zero length array.

The "perceptible initial difference between an empty array and pointer to null" is not lost during serialization/deserialization, it's lost from the moment initial assignment is complete.

Mike Precup
  • 4,148
  • 21
  • 41
  • Up'd because this is in fact how it works. See @mike's comment in my answer to understand the limitations of my solution. – sberry Oct 17 '15 at 05:54
  • What about using a pointer to the slice? Like you would do a pointer to string so it can be nil? Is that possible? – tothemario Oct 04 '16 at 02:19
  • 2
    You can still tell the difference, because you can literally compare against `nil`: https://play.golang.org/p/fxV0atFXJSX – ocket8888 May 08 '20 at 21:58
2

For another solution, we have forked encoding/json to add a new method called MarshalSafeCollections(). This method will marshal Slices/Arrays/Maps as their respective empty values ([]/{}). Since most of our instantiation happens on the data layer we did not want to add code that fixed issues in our http response layer. The changes to the library are minimal and follow go releases.

cbelsole
  • 131
  • 1
  • 6