Trying not to reuse code

The app I’m working on is passing structs back and forward over a web connection using JSON encoding.

There are multiple request and response structs and I want to write a generic Marshal function that will take any of them, marshal it and then return the string.

I’m going to get the terminology wrong here, but in this example, I created a base struct with a Marshal function and Request1 included(?) it. Running the Marshal function doesn’t return any data. I know that this is because none of the attributes in Request1 are in the base so they aren’t seen when the function runs. I’m happy with why this isn’t working.

Request2 has its own Marshal function which works fine.

Is there a way to do some type of inheritance so that I can have a single Marshal function and not have to repeat the same code for every request and response struct?

https://play.golang.org/p/su27NveFKFm

Request1 doesn’t have a Marshall method, you are only marshalling the JSON message, which is empty by definition.

Yes, I understand that, what I’m trying to find out is how to give everything a Marshal function without having to repeat all the code.

If not write such a method at all, but use annotations instead:

type Foo struct {
  Field1 string `json:"field"`
}

I’ve just read a couple of articles on annotations but can’t understand how they would help here. Do you know any examples of using them in the way I would need to?

If you don’t want to repeat the code a simple way is to use a wrapper function over the actual one. You will repeat only the wrapper.

Are either of these correct ways to do things in Go or are they hacks? Neither look like the nice smooth flowing Go code I’m used to.

Yeah, you are right, I missunderstood your issue and only realize now (after I had some sleep and coffee in exactly this order) that its not about marshalling itself, but about having an easy way to marshall into a string.

Sp you could do some interface trickery to make it happen: Go Playground - The Go Programming Language

2 Likes

I might be misunderstanding the question here.

@NobbZ’s reply seems to be a solution to the problem you’re having, but if you’re trying to Marshal an arbitrary struct, this is a solution.

func Marshal(input interface{}) (encoded []byte, err error) {
	encoded, err = json.MarshalIndent(input, "", "\t")
	return
}

That will attempt to encode and return the indented JSON of input. It’ll return []byte if you want that to be a string, you can either cast the resulting value or update the above to the following:

func Marshal(input interface{}) (string, error) {
	encoded, err := json.MarshalIndent(input, "", "\t")

	if err != nil {
		return "", err
	}

	return string(encoded), err
}

https://goplay.space/#0uaoHrbakyX

1 Like

That is exactly what I wanted, thanks. I’m going to use the full version but I’ve simplified it to try to work out what is going on.

Am I right in thinking that as long as whatever is passed in can be marshalled that anything can be passed? And the interface is a generic type that allows this?

https://play.golang.org/p/TAWyw41WdWd

Isn’t that the same as from @NobbZ except shortcutting having to declare JSONMessageMarker and just dropping it straight into the function definition?

Well, my version does return a string directly, not []byte, dunno which one is it you like. Also my version is a bit more typesafe than just using interface{}. At the end you have to shoose on your own which suites better in your project.

OK, I’ll have a play with both of them.

At the moment, I need to be able to pass in my structs and get a string out which I can then use with fmt.Fprintf to send as a HTTP payload.

“At the end you have to shoose on your own which suites better in your project.”

Definitely!

It’s worth mentioning however, @digininja you posted a revised version of @NobbZ example: https://play.golang.org/p/TAWyw41WdWd

In that version, you’ve removed type safety (the foo method) as the JSONMessageMarker doesn’t implement any methods anymore, meaning you’ll get a similar result to using interface{} in any event.

For example, the following will execute without issue in your version:

s3, _ := MarshallJSONMessage([]string{ "hello", "world" })
fmt.Printf("Marshalled: %s\n", s3) // Marshalled: ["hello","world"]

However, in the version @NobbZ originally posted, you will get an error, because type []string doesn’t implement the foo method, as you would expect. To get type safety you need to ensure the JSONMessageMarker interface actually implements something.

I hope this was helpful.

1 Like

That explains the foo(), thanks. I thought that was just a demonstration that I could put functions I wanted shared on the JSONMessage struct.

I think type safety doesn’t matter too much in this instance as I’m fairly sure anything I might pass in would be able to be marshalled, but I will probably stick with the @NobbZ version to save me messing up and trying to pass something I shouldn’t.

That’s a good idea, bare in mind, you’ll need to make sure anything that you wish to Marshal using MarshallJSONMessage will need to implement foo and any other associated interface methods.

You can get a quick overview of interfaces here if you’re not familiar from here https://gobyexample.com/interfaces and here http://www.golangbootcamp.com/book/interfaces.

Looking into the Go native library is super useful as well when it comes to interfaces, look particularly at the Reader and Writer interfaces, they’re used repeatedly and are extremely simple interfaces (each with one method Read and Write respectively if I remember correctly).

1 Like

That’s fine, it will make sure that I build all my structs properly and only pass the right things around.

I’ll have a read of the docs, but now I see the way it works, it makes a lot of sense.

Thanks for the help.

Just for fun, version with wrapper function.

https://play.golang.org/p/uciSSR0c11m

Coming from a Ruby background that does look nicer to me.

Maybe you could either consider a total generic that relieves you from any marshalling concern (as far as possible). Have you heard about gRpc ? It’s really fast and can use Json as message format : https://grpc.io/blog/grpc-with-json
Someone has already done the job of creating the json codec for Go, here it is with example : https://jbrandhorst.com/post/grpc-json/
Have a nice day.