How to make a 2-level depth type definition in a struct? - go

Currently I have the following definition for my structs:
type WholeJson struct {
Features []Temp
}
type Temp struct {
Properties Human
}
type Human struct {
Name string
Age uint
}
Which is working when unmarshaling a JSON string into a variable of type WholeJson, which would have the following structure:
{
"features":[
{
"properties": {
"name": "John Doe",
"age": 50
}
}
]
}
Go Playground sample here: https://play.golang.org/p/3WTLxR0EZWP
But I don't know how to write it in a simpler way. It is obvious that not both WholeJson and Temp are necessary as far as using the information they will hold. The only reason I have Temp is because I simply don't know how to avoid defining it (and still have the program work).
Presumably the Features property of WholeJson would have an array to some interface{}, but I can't nail the syntax. And I'm assuming the code for reading the unmarshalled data (from the playground sample) will stay the same.
How would I "squash" those those two structs into one (the Human struct i'm assuming is okay if it stays on its own), and still have useful data in the end, where I could loop through the features key to go through all the data?

It's OK to define a type for each level of the hierarchy and preferred when constructing values from Go code. The types do not need to be exported.
Use anonymous types to eliminate the defined types:
var sourceData struct {
Features []struct {
Properties Human
}
}
var jsonString string = `{
"features":[
{
"properties": {
"name": "John Doe",
"age": 50
}
}
]
}`
json.Unmarshal([]byte(jsonString), &sourceData)
fmt.Println(sourceData.Features[0].Properties.Name)

Related

GO is a complex nested structure

I wanted to clarify how to set values for
type ElkBulkInsert struct {
Index []struct {
_Index string `json:"_index"`
_Id string `json:"_id"`
} `json:"index"`
}
to make json.Marshall
there were no problems for the usual structure
package main
import (
"encoding/json"
"fmt"
)
type ElkBulkInsert struct {
Index []struct {
_Index string `json:"_index"`
_Id string `json:"_id"`
} `json:"index"`
}
type ElkBulIsertUrl struct {
Url string `json:"url"`
}
func main() {
sf := ElkBulIsertUrl{
Url: "http://mail.ru",
}
dd, _ := json.Marshal(sf)
fmt.Println(string(dd))
}
It's really unclear what you're asking here. Are you asking why the JSON output doesn't match what you expect? Are you unsure how to initialise/set values on the Index field of type []struct{...}?
Because it's quite unclear, I'll attempt to explain why your JSON output may appear to have missing fields (or why not all fields are getting populated), how you can initialise your fields, and how you may be able to improve the types you have.
General answer
If you want to marshal/unmarshal into a struct/type you made, there's a simple rule to keep in mind:
json.Marshal and json.Unmarshal can only access exported fields. An exported field have Capitalised identifiers. Your Index fieldin the ElkBulkInsert is a slice of an anonymous struct, which has no exported fields (_Index and _Id start with an underscore).
Because you're using the json:"_index" tags anyway, the field name itself doesn't have to even resemble the fields of the JSON itself. It's obviously preferable they do in most cases, but it's not required. As an aside: you have a field called Url. It's generally considered better form to follow the standards WRT initialisms, and rename that field to URL:
Words in names that are initialisms or acronyms (e.g. "URL" or "NATO") have a consistent case. For example, "URL" should appear as "URL" or "url" (as in "urlPony", or "URLPony"), never as "Url". Here's an example: ServeHTTP not ServeHttp.
This rule also applies to "ID" when it is short for "identifier," so write "appID" instead of "appId".
Code generated by the protocol buffer compiler is exempt from this rule. Human-written code is held to a higher standard than machine-written code.
With that being said, simply changing the types to this will work:
type ElkBulkInsert struct {
Index []struct {
Index string `json:"_index"`
ID string `json:"_id"`
} `json:"index"`
}
type ElkBulIsertUrl struct {
URL string `json:"url"`
}
Of course, this implies the data for ElkBulkInsert looks something like:
{
"index": [
{
"_index": "foo",
"_id": "bar"
},
{
"_index": "fizz",
"_id": "buzz"
}
]
}
When you want to set values for a structure like this, I generally find it easier to shy away from using anonymous struct fields like the one you have in your Index slice, and use something like:
type ElkInsertIndex struct {
ID string `json:"_id"`
Index string `json:"_index"`
}
type ElkBulkInsert struct {
Index []ElkInsertIndex `json:"index"`
}
This makes it a lot easier to populate the slice:
bulk := ElkBulkInsert{
Index: make([]ElkInsertIndex, 0, 10), // as an example
}
for i := 0; i < 10; i++ {
bulk.Index = append(bulk.Index, ElkInsertIndex{
ID: fmt.Sprintf("%d", i),
Index: fmt.Sprintf("Idx#%d", i), // wherever these values come from
})
}
Even easier (for instance when writing fixtures or unit tests) is to create a pre-populated literal:
data := ElkBulkInsert{
Index: []ElkInsertIndex{
{
ID: "foo",
Index: "bar",
},
{
ID: "fizz",
Index: "buzz",
},
},
}
With your current type, using the anonymous struct type, you can still do the same thing, but it looks messier, and requires more maintenance: you have to repeat the type:
data := ElkBulkInsert{
Index: []struct{
ID string `json:"_id"`
Index string `json:"_index"`
} {
ID: "foo",
Index: "bar",
},
{ // you can omit the field names if you know the order, and initialise all of them
"fizz",
"buzz",
},
}
Omitting field names when initialising in possible in both cases, but I'd advise against it. As fields get added/renamed/moved around over time, maintaining this mess becomes a nightmare. That's also why I'd strongly suggest you use move away from the anonymous struct here. They can be useful in places, but when you're representing a known data-format that comes from, or is sent to an external party (as JSON tends to do), I find it better to have all the relevant types named, labelled, documented somewhere. At the very least, you can add comments to each type, detailing what values it represents, and you can generate a nice, informative godoc from it all.

UUID field within Go Pact consumer test

I'm currently looking at adding Pact testing into my Go code, and i'm getting stuck on how to deal with field types of UUID.
I have the following struct, which I use to deserialise a response from an API to
import (
"github.com/google/uuid"
)
type Foo struct {
ID uuid.UUID `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
}
Now, when I try and write my consumer test, it looks something like this
pact.
AddInteraction().
Given("A result exists").
UponReceiving("A request to get all results").
WithRequest(dsl.Request{
Method: "get",
Path: dsl.String("/v1"),
}).
WillRespondWith(dsl.Response{
Status: 200,
Headers: dsl.MapMatcher{"Content-Type": dsl.String("application/json")},
Body: dsl.Match(&Foo{}),
})
The problem now, is the mocked response comes through as below, where it tries to put an array of bytes in the "id" field, I'm assuming since behind the scenes that is what the google/uuid library stores it as.
{
"id": [
1
],
"name": "string",
"description": "string"
}
Has anyone encountered this? And what would be the best way forward - the only solution I can see is changing my model to be a string, and then manually convert to a UUID within my code.
You currently can't use the Match function this way, as it recurses non primitive structures, albeit it should be possible to override this behaviour with struct tags. Could you please raise a feature request?
The simplest approach is to not use the Match method, and just manually express the contract details in the usual way.
The internal representation of uuid.UUID is type UUID [16]byte but json representation of UUID is string
var u uuid.UUID
u, _ = uuid.Parse("f47ac10b-58cc-0372-8567-0e02b2c3d479")
foo1 := Foo{u, "n", "d"}
res, _ := json.Marshal(foo1)
fmt.Println(string(res))
{
"id": "f47ac10b-58cc-0372-8567-0e02b2c3d479",
"name": "n",
"description": "d"
}
and then load marshaled []byte
var foo Foo
json.Unmarshal(res, &foo)

defining a GraphQL mutation in DGraph that adds a node that is of union type within another node

I am new to GraphQL and Dgraph and have been reading through the manual.
I am following an example in the manual posted here:
https://dgraph.io/docs/graphql/schema/types/#union-type
I am seeking to use a mutation to add a node or list of Nodes and associate them as members acceptable within the addHome input function.
These nodes are however also of type Union and so I need to somehow cast the HomeMember to a Dog and define the category as an animal.
I see someone posted something here but i can't pull the pieces together since the code it suggests appears to already have been generated by DGraph:
https://discuss.dgraph.io/t/union-types-in-graphql/9581
I am unsure but have a feeling I perhaps need to somehow override the input HomeMemberRef which is automatically generated into the schema by DGraph and disable both parrot and human ?
My code so far looks like this:
mutation {
addHome(input: [
{address: "London",
// Here I need to add something a member that casts the
// HomeMember to a Dog, defines the category as an animal and is accepted within
// this addHome input function
}
]) {
home{
address
id
members
}
}
}
Found the answer posted on another page:
https://dgraph.io/docs/graphql/mutations/mutations-overview/
My GraphQL works using this:
mutation {
addHome(input: [
{address: "London",
members: [
{ dogRef: { category: Mammal, breed: "German Shepherd"} },
]
}
]) {
home {
address
members {
... on Dog {
breed
}
... on Parrot {
repeatsWords
}
... on Human {
name
}
}
}
}
}

How to adapt query to API?

I'm trying to wrap my head around GraphQL.
Right now I'm just playing with the public API of Artsy (an art website, playground at https://metaphysics-production.artsy.net). What I want to achieve is following:
I want to get all node types entities without declaring them by hand (is there a shortcut for this)?
I want every node with a field type from which I can read the type, without parsing through imageUrl etc. to fint that out.
What I constructed as of right now is this:
{
search(query: "Berlin", first: 100, page: 1, entities: [ARTIST, ARTWORK, ARTICLE]) {
edges {
node {
displayLabel
imageUrl
href
}
}
}}
Very primitive I guess. Can you guys help me?
TL;DR:
1) There is no shortcut, it's not something GraphQL offers out of the box. Nor is it something I was able to find via their Schema.
2) Their returned node of type Searchable does not contain a property for type that you're looking for. But you can access it via the ... on SearchableItem (union) syntax.
Explanation:
For question 1):
Looking at their schema, you can see that their search query has the following type details:
search(
query: String!
entities: [SearchEntity]
mode: SearchMode
aggregations: [SearchAggregation]
page: Int
after: String
first: Int
before: String
last: Int
): SearchableConnection
The query accepts an entities property of type SearchEntity which looks like this:
enum SearchEntity {
ARTIST
ARTWORK
ARTICLE
CITY
COLLECTION
FAIR
FEATURE
GALLERY
GENE
INSTITUTION
PROFILE
SALE
SHOW
TAG
}
Depending on what your usecase is, if you're constructing this query via some code, then you can find out which SearchEntity values they have:
{
__type(name: "SearchEntity") {
name
enumValues {
name
}
}
}
Which returns:
{
"data": {
"__type": {
"name": "SearchEntity",
"enumValues": [
{
"name": "ARTIST"
},
{
"name": "ARTWORK"
},
...
}
}
}
then store them in an array, omit the quotation marks from the enum and pass the array back to the original query directly as an argument.
Something along the lines of this:
query search($entities: [SearchEntity]) {
search(query: "Berlin", first: 100, page: 1, entities: $entities) {
edges {
node {
displayLabel
imageUrl
href
}
}
}
}
and in your query variables section, you just need to add:
{
"entities": [ARTIST, ARTWORK, ...]
}
As for question 2)
The query itself returns a SearchableConnection object.
type SearchableConnection {
pageInfo: PageInfo!
edges: [SearchableEdge]
pageCursors: PageCursors
totalCount: Int
aggregations: [SearchAggregationResults]
}
Digging deeper, we can see that they have edges, of type SearchableEdge - which is what you're querying.
type SearchableEdge {
node: Searchable
cursor: String!
}
and finally, node of type Searchable which contains the data you're trying to access.
Now, the type Searchable doesn't contain type:
type Searchable {
displayLabel: String
imageUrl: String
href: String
}
But, if you look at where that Searchable type is implemented, you can see SearchableItem - which contains the property of displayType - which doesn't actually exist in Searchable.
You can access the property of SearchableItem and get the displayType, like so:
{
search(query: "Berlin", first: 100, page: 1, entities: [ARTIST, ARTWORK, ARTICLE]) {
edges {
node {
displayLabel
imageUrl
href
... on SearchableItem {
displayType
}
}
}
}
}
and your result will look like this:
{
"data": {
"search": {
"edges": [
{
"node": {
"displayLabel": "Boris Berlin",
"imageUrl": "https://d32dm0rphc51dk.cloudfront.net/CRxSPNyhHKDIonwLKIVmIA/square.jpg",
"href": "/artist/boris-berlin",
"displayType": "Artist"
}
},
...

Is there a better way to declare json variable

Declaring a variable of type map[string]map[string]... is not ideal, is there a better way
snaps := map[string]map[string]map[string]map[string]string{
"distros": {
"aws": {
"eu-west-1": {
"snap-0": "/dev/sdm",
},
"eu-west-2": {
"snap-1": "/dev/sdm",
},
},
},
}
fmt.Println(snaps["distros"]["aws"]["eu-west-1"])
The simplest way would be to use the type map[string]interface{}. Since the empty interface, interface{}, refers to any type and therefore handles the arbitrarily nested nature of JSON.
To do this you'll have to write your literal data as a string first and then parse the string into a Go map.
With that in mind here is a refactor of your example:
first: import "encoding/json", then
snapsStr := `{
"distros": {
"aws": {
"eu-west-1" : {
"snap-0": "/dev/sdm"
},
"eu-west-2": {
"snap-1": "/dev/sdm"
}
}
}
}`
var snaps map[string]interface{}
json.Unmarshal([]byte(snapsStr), &snaps)
And now snaps is as desired.
This is the most generic format for JSON data in Go and is one of the ways that the Go JSON library handles types for JSON. See these docs: https://golang.org/pkg/encoding/json/#Unmarshal

Resources