-
Notifications
You must be signed in to change notification settings - Fork 25k
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
Introduce ToXContentObject interface #22387
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left some minor stuff, some questions about naming, etc. But I think this'll be lovely to have.
I wonder if you can also convert the methods in XContentBuilder
that take a ToXContent
to taking ToXContentObject
instead?
try { | ||
XContentBuilder builder = JsonXContent.contentBuilder(); | ||
if (wrapInObject) { | ||
if (toXContent instanceof ToXContentObject == false) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd probably pull the instanceof check into a boolean variable just to make sure it is only done once.
I keep thinking that we should be able to remove the instanceof check by making another method and we could do that for the most part but I don't think it'd be worth it. At least not for now, anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd probably pull the instanceof check into a boolean variable just to make sure it is only done once.
will do, makes sense.
I keep thinking that we should be able to remove the instanceof check by making another method and we could do that for the most part but I don't think it'd be worth it. At least not for now, anyway.
agreed, the instanceof checks are not nice. Yet they are an improvement compared to the previous boolean flag.
If we had two methods, the method variant that accepts ToXContent
would also accept a ToXContentObject
which would throw error when adding the anonymous object as it already has it. Not too sure. As you said it may stay this way for now.
@@ -26,6 +26,8 @@ | |||
|
|||
/** | |||
* An interface allowing to transfer an object to "XContent" using an {@link XContentBuilder}. | |||
* The output may not be valid alone without a valid ancestor, or without starting and ending an |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about "The output may or may not be a value object. Objects implementing {@link ToXContentObject} output a valid value but those that don't may or may not require emitting a startObject and an endObject."
|
||
/** | ||
* An interface allowing to transfer an object to "XContent" using an {@link XContentBuilder}. | ||
* The difference between {@link ToXContent} and {@link ToXContentObject} is that the former outputs a fragment that |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the former may output a fragment that requires....
* may require to start and end a new anonymous object externally, while the latter guarantees that what gets printed | ||
* out is fully valid syntax without any external addition. | ||
*/ | ||
public interface ToXContentObject extends ToXContent { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if ToXContentValue
is better. The idea being that objects that implement the interface output valid left hand side "values".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do not know. When I read value, I don't think of a complete object. But I do see how this is debatable. I would much rather have ToXContent and ToXContentFragment, but that'd require a huge change that I don't want to make at the moment. How about ToXContentValueObject? :D
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would much rather have ToXContent and ToXContentFragment
That would be the best option, but in the meanwhile I like ToXContentObject
for the marker interface.
public final RestResponse buildResponse(Response response, XContentBuilder builder) throws Exception { | ||
if (wrapInObject()) { | ||
protected final void toXContent(Response response, XContentBuilder builder) throws IOException { | ||
if (response instanceof ToXContentObject == false) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It'd be nice to be able to change this to only take ToXContentObject
one day....
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But that day can wait.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes it would be, we are not that far from that day, yet some more work is required to migrate response that are reused throughout different apis.
c764688
to
d2516ca
Compare
@nik9000 thanks for your review, I have addressed your comments. I will wait for some more comments on naming and the approach taken from others before pushing. I think this is an improvement but there are a few things in this PR that are not super nice (e.g. the instanceof checks) |
I was thinking we could consider those instanceof checks fairly temporary. At some point we can remove the versions that take |
* Return a {@link String} that is the json representation of the provided | ||
* {@link ToXContent}. | ||
* Return a {@link String} that is the json representation of the provided {@link ToXContent}. | ||
* Wraps the output into an anonymous object depending on whether the provided argument implements {@link ToXContentObject} or not | ||
*/ | ||
public static String toString(ToXContent toXContent) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like we could already change this method to
public static String toString(ToXContentObject o) {
return toString(o, false);
}
and instead change all Strings.toString(ToXContent)
calls to Strings.toString(ToXContent, false)
, in which we can just check that when wrapInObject
is true the object does not also implement ToXContentObject
.
This would avoid most instanceof
checks and it prepares the field for a toString(ToXContentFragment o)
method. Both of them would later delegate to a private static toString(ToXContent toXContent, boolean wrapInObject)
...
* may require to start and end a new anonymous object externally, while the latter guarantees that what gets printed | ||
* out is fully valid syntax without any external addition. | ||
*/ | ||
public interface ToXContentObject extends ToXContent { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would much rather have ToXContent and ToXContentFragment
That would be the best option, but in the meanwhile I like ToXContentObject
for the marker interface.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this change as well as the ToXContentObject name. I also left a minor comment.
cb42070
to
f25c498
Compare
The instanceOf checks are gone now. I followed Tanguy's suggestion in |
@s1monw would you mind having a look too please? |
I wonder if should rather have a default method on the interface rather than a marker. The problem with a marker is that you have to do instanceof checks to do the right thing which are easily missable and if you cast up you are lost if you miss them? |
@s1monw good point I agree that the current toString etc. methods will not work for instance if one does
although I was considering as well a default method but I am not super sure if a different method helps. Then you would have two ways of printing out the same object which may get confusing. What about looking into not having ToXContentObject extend ToXContent anymore (and copying the same method)? I think that should be possible in the state that this PR is now. I think that would resolve the ambiguity? |
I was suggesting this: diff --git a/core/src/main/java/org/elasticsearch/common/xcontent/ToXContent.java b/core/src/main/java/org/elasticsearch/common/xcontent/ToXContent.java
index 01111fa..9efdaaa 100644
--- a/core/src/main/java/org/elasticsearch/common/xcontent/ToXContent.java
+++ b/core/src/main/java/org/elasticsearch/common/xcontent/ToXContent.java
@@ -126,4 +126,8 @@ public interface ToXContent {
}
XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException;
+
+ default boolean isFragment() {
+ return true;
+ }
} |
if you don't like this then please make sure you are doing instance of checks where you distinguish. I don't see them yet |
@s1monw I followed your advice to resolve the ambiguity, and also kept the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
61c9f3a
to
1504ba8
Compare
`ToXContentObject` extends `ToXContent` without adding new methods to it, while allowing to mark classes that output complete xcontent objects to distinguish them from classes that require starting and ending an anonymous object externally. Ideally ToXContent would be renamed to ToXContentFragment, but that would be a huge change in our codebase, hence we simply document the fact that toXContent outputs fragments with no guarantees that the output is valid per se without an external ancestor. Relates to elastic#16347
This involved changing also index, delete, update so that bulk can print them out in its own format.
…ToXContentObject This also allows to make RestToXContentListener require ToXContentObject rather than ToXContent
1504ba8
to
8d08dc0
Compare
Related to elastic#22387
ToXContentObject
extendsToXContent
without adding new methods to it, while allowing to mark classes that output complete xcontent objects to distinguish them from classes that require starting and ending an anonymous object externally.Ideally
ToXContent
would be renamed toToXContentFragment
or something along those lines, but that would be a huge change in our codebase, hence we can simply document the fact thatToXContent
outputs fragments with no guarantees that the output is valid per se without an external ancestor.As part of this PR also some of our response classes are migrated to the new interface. In general all of our response should implement
ToXContentObject
, but some don't even implement yetToXContent
, while some that do will be migrated step by step as part of the effort made for REST high level client (essentially while writing parsing code, we can also adjust thetoXContent
methods.RestToXContentListener
,String#toString
andXContentHelper#toXContent
used to take aboolean
argument to indicate whether the output needed to be wrapped into a new object. This decision doesn't have to be made case by case now. We can rely on whether a class implementsToXContent
(fragment) orToXContentObject
(complete valid object) instead.There are many more things that can be done to improve how deal with
ToXContent
classes, like unifying their toStrings output etc. those improvements will come later, one at a time.Relates to # 3889
Relates to #16347