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

Empty array if no embedded representation is added #39

Open
mg-2014 opened this issue Aug 24, 2017 · 3 comments
Open

Empty array if no embedded representation is added #39

mg-2014 opened this issue Aug 24, 2017 · 3 comments

Comments

@mg-2014
Copy link

mg-2014 commented Aug 24, 2017

Hi,

This issue applies to v5.1.1, 5.1.2-SNAPSHOT (and the current "develop" branch).

There seems to be no way to add an empty array as an _embedded object, e.g. with

      ResourceRepresentation< ? > resource = ResourceRepresentation.empty( "/" )
         .withLink( "items", "/items" )
         .withRepresentation( "items", ResourceRepresentation.empty( "/items" )
            .withRel( Rels.collection( "item" ) ) );

      System.out.println( JsonRepresentationWriter.create( new ObjectMapper() ).print( resource ).utf8() );      

I get the following output:

{
  "_links" : {
    "self" : {
      "href" : "/"
    },
    "items" : {
      "href" : "/items"
    }
  },
  "_embedded" : {
    "items" : {
      "_links" : {
        "self" : {
          "href" : "/items"
        }
      }
    }
  }
}

Since I used .withRel( Rels.collection( "item" ) ) I expected the output to include an empty array:

{
  "_links" : {
    "self" : {
      "href" : "/"
    },
    "items" : {
      "href" : "/items"
    }
  },
  "_embedded" : {
    "items" : {
      "_links" : {
        "self" : {
          "href" : "/items"
        }
      },
      "item" : [ ]
    }
  }
}

The code

      ResourceRepresentation< ? > resource = ResourceRepresentation.empty( "/" )
         .withLink( "items", "/items/1" )
         .withRepresentation( "items", ResourceRepresentation.empty( "/items/1" )
            .withRel( Rels.collection( "item" ) )
            .withRepresentation( "item", ResourceRepresentation.empty() ) );

results in "item": [ { } ] which is not correct.

The code

      ResourceRepresentation< ? > resource = ResourceRepresentation.empty( "/" )
         .withLink( "items", "/items/1" )
         .withRepresentation( "items", ResourceRepresentation.empty( "/items/1" )
            .withRel( Rels.collection( "item" ) )
            .withRepresentation( "item", ResourceRepresentation.create( Collections.EMPTY_LIST ) ) );

results in an Exception:

Exception in thread "main" java.lang.IllegalStateException: Unable to serialise a non Object Node
	at com.theoryinpractise.halbuilder5.json.JsonRepresentationWriter.renderJsonProperties(JsonRepresentationWriter.java:151)
...

The code

      ResourceRepresentation< ? > resource = ResourceRepresentation.empty( "/" )
         .withLink( "items", "/items" )
         .withRepresentation( "items",
            ResourceRepresentation.create( "/items", ImmutableMap.of( "item", Collections.EMPTY_LIST ) ) );            

actually provides the expected result although it seems to be more of a work around to set the value of the ResourceRepresentation to an object { "list": [ ] }. The bad thing of this work around is that we will need a check for an empty list beforehand and if it's empty, we create this representation, else we create the "normal" representation. It might be a good idea to make the initial example with .withRel( Rels.collection( "item" ) ) lead to the same result.

Regards,
Markus

@mg-2014 mg-2014 changed the title Unable to create an empty array as an _embedded object. Empty array if no embedded representation is added Aug 24, 2017
@talios
Copy link
Member

talios commented Aug 24, 2017

Correct me if I'm wrong - but you're using two separate rels: items when you add the embedded, but item when you define the rel. That may explain the discrepancy.

You're first example - "item" : [ ] is a field of the represented object, not a rel. So you're expected output isn't really what you'd expect. withRel just defines how a rel is handled when embedded.

@talios
Copy link
Member

talios commented Aug 24, 2017

And yes - you can't embed a non object node - that error should be more gracefully handled, probably at the time of constructing the ResourceRepresentation somehow - tho I suspect that's largely at the mercy of whatever ObjectMapper is passed in. An array might be serialised as an object as:

{
  "0": ....,
  "1": ....
}

@mg-2014
Copy link
Author

mg-2014 commented Aug 25, 2017

Hi Mark,

you're right. The expected output is wrong. I'll need to correct the description.
It may be better to describe what I'm trying to achieve...

We're using a HAL client in the frontend to follow the relations provided by the HAL payload:
https://github.com/aixigo/hal-http-client/
The backend provides the relations and the frontend follows them. If a resource is embedded, the frontend doesn't need to follow a link but use the embedded resource instead. This happens transparently to the Javascript developer. All he needs to do is follow relations.

What we're trying to achieve in the above example is provide a list of items. We're doing this with the following code:

List< Item > items = ImmutableList.of( new Item( "item0" ), new Item( "item1" ) );

ResourceRepresentation< ? > itemsResource = ResourceRepresentation
   .empty( "/items" )
   .withRel( Rels.collection( "item" ) );

for( int i = 0; i < items.size(); ++i ) {
   itemsResource = itemsResource
      .withLink( "item", "/items/" + i )
      .withRepresentation( "item", ResourceRepresentation.create( "/items/" + i, items.get( i ) ) );
}

ResourceRepresentation< ? > resource = ResourceRepresentation.empty( "/" )
   .withLink( "items", "/items" )
   .withRepresentation( "items", itemsResource );

System.out.println( JsonRepresentationWriter.create( new ObjectMapper() ).print( resource ).utf8() );

and we get:

{
  "_links" : {
    "self" : {
      "href" : "/"
    },
    "items" : {
      "href" : "/items"
    }
  },
  "_embedded" : {
    "items" : {
      "_links" : {
        "self" : {
          "href" : "/items"
        },
        "item" : [ {
          "href" : "/items/0"
        }, {
          "href" : "/items/1"
        } ]
      },
      "_embedded" : {
        "item" : [ {
          "name" : "item0",
          "_links" : {
            "self" : {
              "href" : "/items/0"
            }
          }
        }, {
          "name" : "item1",
          "_links" : {
            "self" : {
              "href" : "/items/1"
            }
          }
        } ]
      }
    }
  }
}

The HAL client follows the relation items but doesn't need to do a HTTP request since items is embedded. It contains a list of relations named item. So the HAL client iterates over the list and follows each item but again doesn't need to do a HTTP request since each item is embedded.

But if the list of items is empty, we get:

{
  "_links" : {
    "self" : {
      "href" : "/"
    },
    "items" : {
      "href" : "/items"
    }
  },
  "_embedded" : {
    "items" : {
      "_links" : {
        "self" : {
          "href" : "/items"
        }
      }
    }
  }
}

which for the HAL client means item relation is undefined. Thus the frontend developer always needs to perform a check for the existence of the relation beforehand.
You may argue that if the backend developer is nice he will do the empty list check in the backend and always provide an array. That's what we're trying to achieve. But it would be even better if the backend developer didn't have to do the empty list check (which makes the code less readable) but HalBuilder handles empty lists transparently.

Now back to the expected output. You're right. The expected output in the above description isn't correct. The expected output would contain an empty item array as links and as an embedded resource:

{
  "_links" : {
    "self" : {
      "href" : "/"
    },
    "items" : {
      "href" : "/items"
    }
  },
  "_embedded" : {
    "items" : {
      "_links" : {
        "self" : {
          "href" : "/items"
        },
        "item": [ ]
      },
      "_embedded" : {
        "item" : [ ]
      }
    }
  }
}

I've not corrected the initial description. I guess this comment shows the intention much better than the initial description. It might be better to close this issue and create a new one with this comment as the description?

Regards,
Markus

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