Skip to content
This repository was archived by the owner on Sep 4, 2025. It is now read-only.

Allow parsing of integer array attributes from JSON#73

Open
sbelkin88 wants to merge 3 commits intogoogle:masterfrom
sbelkin88:update_request_parser
Open

Allow parsing of integer array attributes from JSON#73
sbelkin88 wants to merge 3 commits intogoogle:masterfrom
sbelkin88:update_request_parser

Conversation

@sbelkin88
Copy link
Copy Markdown
Contributor

No description provided.

@aren55555
Copy link
Copy Markdown
Contributor

There is a larger problem here than just supporting []int; this library needs to supports any deeply nested type in the "attributes" section, but it does not allow this as it stands.

Example:

type Position struct {
	Latitude  string
	Longitude string
}

type City struct {
	ID       int      `jsonapi:"primary,city"`
	Location Position `jsonapi:"attr,location"`
}
{
    "data": {
        "type": "city",
        "id": "1",
        "attributes": {
            "name": "Toronto",
            "location": {
                "lat": "43.6532° N",
                "lng": "79.3832° W"
            },
            ... other attributes etc
        },
        ... relations etc
    }
}

@sbelkin88
Copy link
Copy Markdown
Contributor Author

sbelkin88 commented Feb 3, 2017

@aren55555 you are correct, this is just supporting a top-level attribute field that is of []int type. We've been using a fix locally to account for nested pointers here by adding the following in the request parsing. I'm happy to include it here if you think it would be beneficial.

// Field was a Pointer type
if fieldValue.Kind() == reflect.Ptr {
    var concreteVal reflect.Value

    switch cVal := val.(type) {
    case string:
        concreteVal = reflect.ValueOf(&cVal)
    case bool:
        concreteVal = reflect.ValueOf(&cVal)
    case complex64:
        concreteVal = reflect.ValueOf(&cVal)
    case complex128:
        concreteVal = reflect.ValueOf(&cVal)
    case uintptr:
        concreteVal = reflect.ValueOf(&cVal)

    // this is the case we added for our local use
    case interface{}:
        n := new(Node)

        var ok bool
        n.Attributes, ok = val.(map[string]interface{})
        if !ok {
            er = ErrUnsupportedPtrType
            break
        }

        if err := unmarshalNode(n, fieldValue, included); err != nil {
            er = ErrUnsupportedPtrType
            break
        }
        concreteVal = fieldValue
    default:
        return ErrUnsupportedPtrType
    }

    if fieldValue.Type() != concreteVal.Type() {
        return ErrUnsupportedPtrType
    }

    fieldValue.Set(concreteVal)
    continue
}

Comment thread response_test.go
CurrentPostID int `jsonapi:"attr,current_post_id"`
CreatedAt time.Time `jsonapi:"attr,created_at"`
ViewCount int `jsonapi:"attr,view_count"`
BookmarkedPages []int `jsonapi:"attr,bookmarked_pages"`
Copy link
Copy Markdown
Contributor

@aren55555 aren55555 Feb 3, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did this work for serialization/marshaling? ie did it appear in the json payload as expected?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this serializes correctly. I just pushed an update including a test for this.

@aren55555
Copy link
Copy Markdown
Contributor

aren55555 commented Feb 3, 2017

I think adding []int just leads me to ask, why not support []uint, []string, or []time.Time, etc also. To me a more general approach to this problem that covers all of these cases (plus others) would be more appropriate than adding one off support for say just []int.

@sbelkin88
Copy link
Copy Markdown
Contributor Author

There is already support for []string, and we ran into a use case for []int which is why we added it.

@svperfecta
Copy link
Copy Markdown

svperfecta commented Feb 11, 2017

We sorta do this already by just making a property like this:

Stats        map[string]interface{} `json:"stats" jsonapi:"attr,stats" validate:"required"`

At this point anything posted underneath that property, as long as its valid JSON, will end up here.

We keep anything important specifically at the top level, and otherwise use related objects.

PS: For storage, we just convert this to a byte array.

@bfitzsimmons
Copy link
Copy Markdown

@genexp Are you doing that with the library as it current exists, or are you working with a fork?

@svperfecta
Copy link
Copy Markdown

hey @bfitzsimmons as it exists!

@bfitzsimmons
Copy link
Copy Markdown

bfitzsimmons commented May 11, 2017

@genexp Thanks for the quick response.

Hmmm...

I thought I was trying what you had posted, but I was actually attempting to Unmarshal into a slice of maps. That's definitely not working.

Example:

Here's the struct (note the slice of maps):

type Payload struct {
	Field1 string `jsonapi:"attr,field1"`
	Field2 string `jsonapi:"attr,field2"`
	Stuff []map[string]interface{} `jsonapi:"attr,stuff"`
}

Here's the POST payload.

{
    "data": {
        "type": "stuffs",
        "attributes": {
            "field1": "77005db9-bceb-4f54-a045-26399ba25db2",
            "field2": "2cfe125d-b0a5-493b-82e6-69c3f6a45e9a",
            "stuff": [
            	{
            		"key": "foo",
            		"value": "yeehaw",
            		"type": "string"
            	},
        	 	{
            		"key": "bar",
            		"value": "true",
            		"type": "bool"
            	},
            	{
            		"key": "baz",
            		"value": "42",
            		"type": "int"
            	}
            ]
        }
    }
}

Should this be working as well? If not, is there a workaround that I should be considering?

@bfitzsimmons
Copy link
Copy Markdown

bfitzsimmons commented May 11, 2017

I added the following to unmarshalNode; it's working, and tests are still passing. Nice.

if fieldValue.Type() == reflect.TypeOf([]map[string]interface{}{}) {
    values := make([]map[string]interface{}, v.Len())
    for i := 0; i < v.Len(); i++ {
        values[i] = v.Index(i).Interface().(map[string]interface{})
    }

    fieldValue.Set(reflect.ValueOf(values))

    continue
}

Good idea? Bad idea? Should I write an appropriate test and submit it?

@svperfecta
Copy link
Copy Markdown

svperfecta commented May 11, 2017

Seems good to me! Essentially your allowing the root node being mapped to Be a JSON array instead of a JSON object. I dig it!

@denouche
Copy link
Copy Markdown

denouche commented Apr 23, 2018

Hi, Will this PR be merged?

@timrourke
Copy link
Copy Markdown

@sbelkin88 Hi there! This looks like an awesome contribution, though I just noticed there are conflicts to resolve. I wonder if you have a moment, could you try resolving these conflicts? Would love to see this contribution make it into the library for my own sake! 😛

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants