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

org.springframework.hateoas.Resources should serialize content as arrays, not lists. #650

Open
douglassparker opened this issue Oct 14, 2017 · 4 comments
Labels

Comments

@douglassparker
Copy link

Jackson is better at serializing arrays than lists since there is no type erasure. When a GET method returns a Resources object, I see something like:

{ 
    "_embedded": {
        "hashMapList": [
            {
                "fld1": "a",
                "_links": {
                    "self": {
                        "href": "http://..."
                    }
                }
            }
        ]
    }
}

The "hashMapList" sticks out. I would rather see this:

{
    "content": [
        {
            "fld1": "a",
            "_links": {
                "self": {
                    "href": "http://..."
                }
            }
        }
    ]
}

In retrospect, Resources.getContents() should have probably returned T[] instead of Collection<T>.

My workaround was to create a class called ArrayResources that extended Resources with the following code:

	@Override
	@JsonIgnore
	public Collection<T> getContent() {
		return super.getContent();
	}
	
	@XmlAnyElement
	@XmlElementWrapper
	@JsonProperty("content")	
	@SuppressWarnings("unchecked")
	public T[] getContentArray() {
		Collection<T> content = getContent();
		return content == null ? null :
			(T[]) content.toArray();
	}

I also had to write ArrayPagedResources that duplicated most of the code in PagedResources. Finally I provided static initializers that I use in my Controller classes right before returning.

ArrayResources.from(Resources)
PagedArrayResources.from(PagedResources)

Not the most elegant solution, but it got me what I want. Ideally, I would like to see the code for Resources changed as shown above. If maintaining backward compatibility is vital, perhaps a mixin similar to ResourcesMixin could provide this as an optional feature. (I tried to do this myself, but I never got it to work.) If a mixin could be developed perhaps an attribute would be appropriate, e.g., @EnableResourcesArraySerialization.

If there is an easier way to accomplish this that I am missing, I welcome suggestions. Kudos for all your great work.

@gregturn
Copy link
Contributor

gregturn commented Jan 15, 2019

Your desire to replace _embedded.hashMapList with content containing an array violates the HAL spec.

Section 4.1.2 clearly states that _embedded...

...is an object whose property names are link relation types (a defined by [RFC5988]) and values are either a Resource Object or an array of Resource Objects.

@douglassparker
Copy link
Author

douglassparker commented Jan 16, 2019

There is no mention of "hashMapList" anywhere in the HAL spec. The sample document provided with the spec looks like this:

     "_links": {
       "self": { "href": "/orders" },
       "next": { "href": "/orders?page=2" },
       "find": { "href": "/orders{?id}", "templated": true }
     },
     "_embedded": {
       "orders": [{
           "_links": {
             "self": { "href": "/orders/123" },
             "basket": { "href": "/baskets/98712" },
             "customer": { "href": "/customers/7809" }
           },
           "total": 30.00,
           "currency": "USD",
           "status": "shipped"
         },{
           "_links": {
             "self": { "href": "/orders/124" },
             "basket": { "href": "/baskets/97213" },
             "customer": { "href": "/customers/12369" }
           },
           "total": 20.00,
           "currency": "USD",
           "status": "processing"
       }]
     },
     "currentlyProcessing": 14,
     "shippedToday": 20
   }

This is what I want. When writing about list vs array, I was speaking of Java. JSON (and hence HAL) makes no distinction between a list and an array. I don't wish to be difficult, but I don't see how you can seriously argue that Spring Hateous is correctly serializing HAL content. The "hashMapList" should NOT be there. The list of resources should be a child of the "_embedded" element.

@gregturn
Copy link
Contributor

The spec example you’ve cited shows “orders” as a property name inside _embedded. That is a link relation which itself contains the array of subresources. The output of Spring HATEOAS in your initial example has hashMapList as the link relation.

It can be confusing how to serialize properly. HAL has a simple structure in that it doesn’t look that different from bare JSON. For a single item resource you just start stocking property names and their corresponding values in.

But if you were to encode a collection of resources what’s at the root? HAL says to use _embedded as an object and link relations.

A good test of HAL compliance is to visit the doc using the HAL Browser crafted by the same people that wrote the spec.

@gregturn
Copy link
Contributor

To unravel this one, it would be useful to the see the actual domain object being returned.

Neither hashMapList nor orders are part of the HAL spec. They are both examples of domain objects being rendered.

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

No branches or pull requests

2 participants