This is a proposal to support a new Marshaler interface with a 
context.Context in encoding/json.

 

*Use cases:*

Many REST APIs include meta-data that controls which attributes should be 
included and omitted in the HTTP response body. These controls can be 
static, meaning the serialization behavior is specified at design time and 
does not change at runtime. For example, some attributes may be declared as 
read-only, read-write, read-on-create, write-only, create-only or 
no-access. A write-only property would be sent by a client in the POST 
request, and it would not be serialized in the response.

In other cases, the serialization behavior is dynamic and determined at 
runtime, based on some contextual information. For example, the OData 
$select operator is used by clients to specify which attributes should be 
returned in the response body, and other attributes are supposed to be 
omitted, which is useful for IoT and mobile applications. As another 
example, regulations and security policies may specify that some attributes 
must be masked or anonymized. The data masking and anonymization behavior 
may depend on the data sensitivity level, the role of the user accessing 
the data, and other runtime attributes. Specific examples of sensitive data 
are credit card numbers, IP addresses, serial numbers, and 
Personally-Identifiable Information. Furthermore, in a SaaS environment, 
the same API may be used by multiple end-users, and each end-user has the 
ability to assign a sensitivity level to various attributes.

 

*Solutions with existing go runtime:*

There are multiple approaches to solve these use cases. For static use 
cases, one approach is to create separate Go structs for each desired 
serialization output (HTTP request, HTTP response, data serialization to 
back-end services, etc). It is also possible to create multiple REST paths, 
each path providing different levels of access to the data, and assigning 
authorization rules, but this is still fairly static and must be determined 
at build time. For dynamic use cases, there is a combinatorial effect, so 
it's not practical to create separate go structs for each possible 
combination. Other serialization approaches include the use of reflection, 
manipulating JSON documents with map[string]interface{}, or creating an 
enhanced implementation of encoding/json that provides support for more 
customization.

 

*Proposed solution:*

One approach that would really help to reduce the amount of glue code is to 
add a context to the json.Marshaler interface:

   MarshalJSON(ctx context.Context) ([]byte, error)

Of course, it is not possible to add the argument to the existing 
MarshalJSON() method, as this would break the backward compatibility 
promise of the json.Marshaler interface. But it is possible to create a new 
MarshalerWithContext interface. The interface specifies a 
MarshalJsonWithContext method that takes a context.Context argument. With a 
context.Context, custom marshalers can access the context and determine how 
to marshal the objects at run-time.

To validate this approach, I tested a modified version of json/encode.go,. 
The existing MarshalJSON method is still supported and the package remains 
backward-compatible. When the new interface is implemented by a particular 
receiver, encode.go determines the receiver implements the new interface 
and calls it with the caller's context. I found this approach makes it 
possible to write much more concise and readable application code, compared 
to any other approach we have tried.


*Next steps:*

I've seen several variants of this request in forums and github. Requests 
have been closed because of a lack of valid use cases, backward 
compatibility or other issues. Is it worthwhile to proceed with a proposed 
contribution? Or do golang maintainers strongly believe encoding/json 
should not support passing a context.Context to the custom marshalers?

Thank you. Sebastien

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to