Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom marshal on a field basis #3643

Open
AnthonyHewins opened this issue Oct 3, 2023 · 1 comment
Open

Custom marshal on a field basis #3643

AnthonyHewins opened this issue Oct 3, 2023 · 1 comment

Comments

@AnthonyHewins
Copy link

🚀 Feature

Is it possible to hook on a message type and have a custom marshal/unmarshal in the gateway? Here's what I mean, if I define:

message TypeExample {
    int x =  1;
}

message Nested {
    TypeExample y = 1;
}

And then I call the gateway so I get my JSON representation for Nested, I will get {"y":{"x":<some int>}}. What if I want to change how TypeExample is marshaled? Like if I wanted it to become something like this instead

{"y": {"somethingTotallyDifferent": {}}}

Here's the use case: if you try to use GeoJSON with gRPC gateway, it is a terrible mess and it doesn't translate. The geoJSON spec doesn't fit the strict typing for protobuf because geoJSON is a type union

{
    "type": "Point" | "LineString" | ...
    "coordinates": <single nested float array> | <double nested float array> | <triple nested float array> | <quad nested float array>
}

This just doesn't work with protobuf so we are forced to use something like this which is a flat array of point values, which works totally fine for gRPC, but it really falls short for REST clients that want geoJSON. So basically I want to be able to have the REST clients send me geoJSON, then I write a mapping function that turns the geoJSON into the flat array of points which works for my gRPC implementation and keeps me compliant with the geoJSON spec

@johanbrandhorst
Copy link
Collaborator

Hi Anthony,

Thanks for your issue report. I particularly appreciate that you both outlined the immediate problem and the underlying problem.

To answer your first question, this is possible, but you will need a custom marshaler. What you can do is create a small custom marshaler that wraps the default marshaler, and adds a special case for a specific type at runtime, like so:

type myGeoJSONMarshaler struct {
	runtime.HTTPBodyMarshaler
}

func (m *myGeoJSONMarshaler) Marshal(v interface{}) ([]byte, error) {
	if t, ok := v.(geopn.Value); ok {
	    // custom marshaling to []byte
	    return
	}
	return m.HTTPBodyMarshaler.Marshal(v)
}

To answer the underlying problem, have you explored using the google.protobuf.Struct well known types? This allows arbitrary JSON, but requires more complicated runtime logic. You can use it via the google/protobuf/struct.proto import. Googling will help you find examples. It won't serve you when generating OpenAPI specs but it sounds to me like that's not what you're worried about anyway.

Good luck, and don't hesitate to reach out again if you need more help!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants