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

feat: GsonFactory to have read leniency option #1819

Merged
merged 9 commits into from
Feb 24, 2023

Conversation

suztomo
Copy link
Member

@suztomo suztomo commented Feb 21, 2023

Fixes b/269515918, where the prefix )]}'\n for Cross-site script inclusion (XSSI) didn't work for GsonFactory.

Users will call GsonFactory.builder().setReadLeniency(true).build() when they want this library to process JSON input that may have the prefix.

  • For this pull request, it controlls only the leniency when reading input JSON. This design withReadLeniency() gives flexibility for us to choose writeLeniency in future if necessary. (It's possible that read leniency is true and write leniency is false.)
  • It does not affect existing usage of GsonFactory. (No global state in GsonFactory)

BEGIN_COMMIT_OVERRIDE
feat: GsonFactory to have read leniency option via GsonFactory.builder().setReadLeniency(true).build()
END_COMMIT_OVERRIDE

@suztomo suztomo requested a review from a team as a code owner February 21, 2023 15:52
@product-auto-label product-auto-label bot added the size: s Pull request size is small. label Feb 21, 2023
@product-auto-label product-auto-label bot added size: m Pull request size is medium. and removed size: s Pull request size is small. labels Feb 21, 2023
@suztomo
Copy link
Member Author

suztomo commented Feb 21, 2023

lint (non required) fails:

Error:  Found 1 non-complying files, failing build
Error:  To fix formatting errors, run "mvn com.coveo:fmt-maven-plugin:format"
Error:  Non complying file: /home/runner/work/google-http-java-client/google-http-java-client/google-http-client/src/test/java/com/google/api/client/http/HttpRequestTracingTest.java

This is irrelevant to this pull request.

@suztomo suztomo requested review from meltsufin and a team February 21, 2023 16:07
return readLeniency;
}

private static GsonFactory newInstance(GsonFactory gsonFactory) {
Copy link
Member Author

Choose a reason for hiding this comment

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

As per Effective Java's clone section, creating a factory method. I don't think a subclass needs to access this method.


/** Returns a copy of GsonFactory instance which is lenient when reading JSON value. */
public GsonFactory withReadLeniency() {
GsonFactory copy = newInstance(this);
Copy link
Contributor

Choose a reason for hiding this comment

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

If we're supporting the subclassing use-case, I don't see how this works. Is the intent that subclasses are required to override this function? (If so, we're introducing a backwardly incompatible change to the subclass contract.)

As an example:

// Logs all method invocations.
public class LoggingGsonFactory extends GsonFactory { /* ... */ }

// In some 'main'-type class of the application...
public static GsonFactory createGsonFactory() { return new LoggingGsonFactory(); }

// Then elsewhere...
private GsonFactory gsonFactory = createGsonFactory().withReadLeniency(); 
// Oops! No more logging.

Copy link
Member Author

Choose a reason for hiding this comment

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

Good point. Then subclasses needs to override newInstance. I'm making newInstance protected.

Copy link
Member Author

Choose a reason for hiding this comment

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

But now it's static.

Copy link
Contributor

Choose a reason for hiding this comment

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

Consider avoiding the copy problem altogether.

private boolean isLenient;
public GsonFactory() { this(false); }
public GsonFactory(boolean isLenient) { this.isLenient = isLenient; }
// No withReadLeniency() method.

Copy link
Member Author

@suztomo suztomo Feb 23, 2023

Choose a reason for hiding this comment

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

The boolean flag in the constructor is not good (Avoid boolean parameter go/java-practices/boolean-parameters). It's ambiguous, especially when we want to introduce leniency in writing JSON.

Copy link
Member Author

@suztomo suztomo Feb 23, 2023

Choose a reason for hiding this comment

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

Added * <p>Subclasses should not call this method. Set {@code readLeniency} field to {@code true} instead..

Copy link
Contributor

Choose a reason for hiding this comment

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

"Subclasses should not call this method" is fine if you know you're dealing with a subclass. The point of polymorphism is that you shouldn't care.

There's other ways to take in a setting besides using a boolean if you feel it goes against best practices.

public enum Leniency {
  LENIENT, STRICT
}
private final Leniency leniency;
public GsonFactory() { this(Leniency.STRICT); }
public GsonFactory(Leniency leniency) { this.leniency = Objects.requireNonNull(leniency); }

or a Builder, or a Settings object parameter.

Copy link
Member Author

Choose a reason for hiding this comment

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

As talked, we may want to add more parameters (such as writeLeniency) in future and having different contstructors for different parameter is not good.

I'm bringing Builder class.

Copy link
Member Author

@suztomo suztomo left a comment

Choose a reason for hiding this comment

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


/** Returns a copy of GsonFactory instance which is lenient when reading JSON value. */
public GsonFactory withReadLeniency() {
GsonFactory copy = newInstance(this);
Copy link
Member Author

@suztomo suztomo Feb 23, 2023

Choose a reason for hiding this comment

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

Added * <p>Subclasses should not call this method. Set {@code readLeniency} field to {@code true} instead..


public final void testReaderLeniency_lenient() throws IOException {
JsonObjectParser parser =
new JsonObjectParser(GsonFactory.builder().setReadLeniency(true).build());
Copy link
Member Author

Choose a reason for hiding this comment

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

Memo: This is how to create GsonFactory with readLeniency true.

Copy link
Member Author

@suztomo suztomo left a comment

Choose a reason for hiding this comment

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


public class GsonParserTest extends AbstractJsonParserTest {

@Override
protected JsonFactory newJsonFactory() {
return new GsonFactory();
}

public void testParse_leniency() throws IOException {
Copy link
Member Author

Choose a reason for hiding this comment

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

This wasn't supposed to come here.

@suztomo suztomo merged commit 00d61b9 into googleapis:main Feb 24, 2023
gcf-owl-bot bot added a commit that referenced this pull request Jun 27, 2023
This also changes the JDK distribution from zulu to temurin
https://github.com/actions/setup-java#eclipse-temurin
Source-Link: googleapis/synthtool@ef9fe2e
Post-Processor: gcr.io/cloud-devrel-public-resources/owlbot-java:latest@sha256:31c8276a1bfb43766597d32645721c029cb94571f1b8d996cb2c290744fe52f9
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
size: m Pull request size is medium.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants