Skip to content

Commit

Permalink
Add JsonArray.asList and JsonObject.asMap view methods (#2225)
Browse files Browse the repository at this point in the history
* Add `JsonArray.asList` and `JsonObject.asMap` view methods

* Address review comments
  • Loading branch information
Marcono1234 authored Oct 16, 2022
1 parent c2458bf commit 954d526
Show file tree
Hide file tree
Showing 8 changed files with 904 additions and 166 deletions.
20 changes: 19 additions & 1 deletion gson/src/main/java/com/google/gson/JsonArray.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.google.gson;

import com.google.gson.internal.NonNullElementWrapperList;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
Expand All @@ -28,11 +29,14 @@
* elements are added is preserved. This class does not support {@code null} values. If {@code null}
* is provided as element argument to any of the methods, it is converted to a {@link JsonNull}.
*
* <p>{@code JsonArray} only implements the {@link Iterable} interface but not the {@link List}
* interface. A {@code List} view of it can be obtained with {@link #asList()}.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
public final class JsonArray extends JsonElement implements Iterable<JsonElement> {
private final List<JsonElement> elements;
private final ArrayList<JsonElement> elements;

/**
* Creates an empty JsonArray.
Expand Down Expand Up @@ -393,6 +397,20 @@ public boolean getAsBoolean() {
return getAsSingleElement().getAsBoolean();
}

/**
* Returns a mutable {@link List} view of this {@code JsonArray}. Changes to the {@code List}
* are visible in this {@code JsonArray} and the other way around.
*
* <p>The {@code List} does not permit {@code null} elements. Unlike {@code JsonArray}'s
* {@code null} handling, a {@link NullPointerException} is thrown when trying to add {@code null}.
* Use {@link JsonNull} for JSON null values.
*
* @return mutable {@code List} view
*/
public List<JsonElement> asList() {
return new NonNullElementWrapperList<>(elements);
}

/**
* Returns whether the other object is equal to this. This method only considers
* the other object to be equal if it is an instance of {@code JsonArray} and has
Expand Down
18 changes: 18 additions & 0 deletions gson/src/main/java/com/google/gson/JsonObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@
* This class does not support {@code null} values. If {@code null} is provided as value argument
* to any of the methods, it is converted to a {@link JsonNull}.
*
* <p>{@code JsonObject} does not implement the {@link Map} interface, but a {@code Map} view
* of it can be obtained with {@link #asMap()}.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
Expand Down Expand Up @@ -208,6 +211,21 @@ public JsonObject getAsJsonObject(String memberName) {
return (JsonObject) members.get(memberName);
}

/**
* Returns a mutable {@link Map} view of this {@code JsonObject}. Changes to the {@code Map}
* are visible in this {@code JsonObject} and the other way around.
*
* <p>The {@code Map} does not permit {@code null} keys or values. Unlike {@code JsonObject}'s
* {@code null} handling, a {@link NullPointerException} is thrown when trying to add {@code null}.
* Use {@link JsonNull} for JSON null values.
*
* @return mutable {@code Map} view
*/
public Map<String, JsonElement> asMap() {
// It is safe to expose the underlying map because it disallows null keys and values
return members;
}

/**
* Returns whether the other object is equal to this. This method only considers
* the other object to be equal if it is an instance of {@code JsonObject} and has
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package com.google.gson.internal;

import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.RandomAccess;

/**
* {@link List} which wraps another {@code List} but prevents insertion of
* {@code null} elements. Methods which only perform checks with the element
* argument (e.g. {@link #contains(Object)}) do not throw exceptions for
* {@code null} arguments.
*/
public class NonNullElementWrapperList<E> extends AbstractList<E> implements RandomAccess {
// Explicitly specify ArrayList as type to guarantee that delegate implements RandomAccess
private final ArrayList<E> delegate;

public NonNullElementWrapperList(ArrayList<E> delegate) {
this.delegate = Objects.requireNonNull(delegate);
}

@Override public E get(int index) {
return delegate.get(index);
}

@Override public int size() {
return delegate.size();
}

private E nonNull(E element) {
if (element == null) {
throw new NullPointerException("Element must be non-null");
}
return element;
}

@Override public E set(int index, E element) {
return delegate.set(index, nonNull(element));
}

@Override public void add(int index, E element) {
delegate.add(index, nonNull(element));
}

@Override public E remove(int index) {
return delegate.remove(index);
}

/* The following methods are overridden because their default implementation is inefficient */

@Override public void clear() {
delegate.clear();
}

@Override public boolean remove(Object o) {
return delegate.remove(o);
}

@Override public boolean removeAll(Collection<?> c) {
return delegate.removeAll(c);
}

@Override public boolean retainAll(Collection<?> c) {
return delegate.retainAll(c);
}

@Override public boolean contains(Object o) {
return delegate.contains(o);
}

@Override public int indexOf(Object o) {
return delegate.indexOf(o);
}

@Override public int lastIndexOf(Object o) {
return delegate.lastIndexOf(o);
}

@Override public Object[] toArray() {
return delegate.toArray();
}

@Override public <T> T[] toArray(T[] a) {
return delegate.toArray(a);
}

@Override public boolean equals(Object o) {
return delegate.equals(o);
}

@Override public int hashCode() {
return delegate.hashCode();
}

// TODO: Once Gson targets Java 8 also override List.sort
}
Loading

0 comments on commit 954d526

Please sign in to comment.