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

Allow for-each loops with object nodes #1590

Closed
wants to merge 3 commits into from
Closed

Conversation

kno10
Copy link
Contributor

@kno10 kno10 commented Apr 5, 2017

This change allows using the much more elegant for-each constructs:

for(Map.Entry<String, JsonNode> enry : objectNode.fieldSet())

and

for(String field : objectNode.fieldNamesSet())

in addition to the current iterator-based access, which is more code intensive:

Iterator<String> fieldNameIterator = objectNode.fieldNames();
while(fieldNameIterator.hasNext()) {
  String fieldName = fieldNameIterator.next();
}

and

Iterator<Map.Entry<String, JsonNode>> entryIterator = objectNode.fields();
while(entryIterator.hasNext()) {
  Map.Entry<String, JsonNode> entry = entryIterator.next();
}

IMPORTANT: This also needs an addition to TreeNode in jackson-core, to add this interface method:

    /**
     * Method for accessing names of all fields for this node, iff
     * this node is an Object node. Number of field names accessible
     * will be {@link #size}.
     */
    Set<String> fieldNamesSet();

or the @Override needs to be removed from this method in JsonNode.

In my opinion (you may want to consider this for version 3?), the Iterator based methods should be replaced with Iterable methods completely. I.e. if API breaking changes are acceptable, I would make fields() return an Iterable<Map.Entry<String, JsonNode>> rather than an iterator, as .fieldNames().iterator() is exactly equivalent; and fieldNames() return Iterable<String> rather than an iterator for the same reason. Java allows foreach loops for Iterable, but not for Iterator.

This change allows using the much more elegant for-each constructs:
```
for(String field : objectNode.fieldNamesSet())
```
and
```
for(Map.Entry<String, JsonNode> enry : objectNode.fieldSet())
```
in addition to the current iterator-based access, which is more code intensive:
```
Iterator<String> fieldNameIterator = objectNode.fieldNames();
while(fieldNameIterator.hasNext()) {
  String fieldName = fieldNameIterator.next();
}
```
and
```
Iterator<Map.Entry<String, JsonNode>> entryIterator = objectNode.fields();
while(entryIterator.hasNext()) {
  Map.Entry<String, JsonNode> entry = entryIterator.next();
}
```
Add fieldNamesSet() and fieldsSet() top JsonNode.
* @return Set that can be used to traverse all key/value pairs for
* object nodes; empty set (no contents) for other types
*/
public Set<Map.Entry<String, JsonNode>> fields() {
Copy link
Member

Choose a reason for hiding this comment

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

I think this should be fieldSet, as there is existing fields method (that returns Iterator)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry, yes. This is supposed to be fieldSet.

@cowtowncoder
Copy link
Member

Yes, I concur in all points. Addition of Iterator methods was sub-optimal in hindsight, compared to exposing something to support foreach.

The only change is, I think, that due to backwards compatibility concerns, method probably should not be added in TreeNode until 3.0. While there is no guarantee that cross-minor version components work together, we try to keep compatibility between "adjacent" minor versions at very least, and jackson-core is dependency for pretty much everything.
So, adding a new method in interface needs to happen gradually: first adding methods in implementations and then on a later minor version demote it in interface.
In this case 3.0 being (most likely) the next version this would work well too.

Actually come to think of it; instead of Set, I think return values should be Iterables?
Or at least Collections. While in practice most implementation use Sets, I think this is not guaranteed so it seems better to give little bit of flexibility for implementations.

@kno10
Copy link
Contributor Author

kno10 commented May 5, 2017

I chose Set because I modeled this after the Map interface of Java, which has entrySet(), keySet() etc.; but yes, I agree that Collection (if it allows modification!) or Iterable may be more appropriate.

For 3.0, I would suggest to kick the Iterator, as any Iterable or Collection will allow access to an Iterator if desired; on a new API, node.fields().iterator() should be the recommended replacement.

@cowtowncoder cowtowncoder added the 3.x Issues to be only tackled for Jackson 3.x, not 2.x label Jul 10, 2017
@cowtowncoder
Copy link
Member

Yes, agreed, for 3.x can and should do bigger clean up. I'll tag this as 3.x change.

@cowtowncoder
Copy link
Member

I think this is mostly obsoleted by #3809 (which adds JsonNode.properties()) -- although if alternative was needed for field/property names, could have another PR for just that part: if so, should be called propertyNames() to follow (new) convention of Object entries being called "properties" in all new(er) parts of API.

Closing this PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3.x Issues to be only tackled for Jackson 3.x, not 2.x
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants