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

Replace 'org.json:json' lib with 'com.google.code.gson' lib #1437

Merged
merged 10 commits into from Mar 3, 2021

Conversation

yongyan-gh
Copy link
Contributor

This is for issue #1279

  • Some of Gson classes/methods doesn't support fluent syntax now, has to refactor a little bit.
  • org.json:json has append() / putOpt() can handle empty/null object/array, which need to manually handle in Gson.

@yongyan-gh
Copy link
Contributor Author

Attached the Sarif log generated with this change (using com.google.code.gson)
also Sarif log generated using current SpotBugs (org.json:json) for a comparison reference.
GsonSarifLog.zip

@KengoTODA pls review.
cc @michaelcfanning @eddynaka

Copy link
Member

@KengoTODA KengoTODA left a comment

Choose a reason for hiding this comment

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

Thanks for your contribution! Please check my comments for maintainability.

Also, please put https://github.com/google/gson/blob/gson-parent-2.8.6/LICENSE into spotbugs/licenses directory to distribute.

extensionJson.addProperty("informationUri", informationUri.toString());
}
if (organization != null) {
extensionJson.addProperty("organization", String.valueOf(organization));
Copy link
Member

Choose a reason for hiding this comment

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

Is are any reason to prefer String.valueOf( ... ) over organization.toString()? If so, I want to make the intention clear.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.
organization was declared as Object it should be String

public String toJSONString() {
return String.format("\"%s\"", name().toLowerCase());
public String toJsonString() {
return name().toLowerCase();
Copy link
Member

Choose a reason for hiding this comment

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

Do we need this method? The old one was provided to configure the org.json's serializer.
So I assume what we need is not this method but similar way to configure the serialier, like SerializedName.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added SerializedName annotation to the enum. Its useful if we use Gson toJson/fromJson methods to serialize/deserialize objects.

Current we create/setup JsonObject instead serializing object. e.g.

        JsonObject result = new JsonObject();
        result.addProperty("ruleId", ruleId);
        result.addProperty("ruleIndex", ruleIndex);;
        result.addProperty("level", level.toJsonString());

I think after POJO classes are introduced we should be able to use object serialization/deserialization and not need this method anymore

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Also tried
return new Gson().toJson(this);
but it returned string with double quotes "\"warning"\", but we need string without double quotes "warning"

"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json");
processRuns(jsonWriter);
jsonWriter.endObject();
getBugCollection().bugsPopulated();
} catch (IOException e) {
e.printStackTrace(System.err);
Copy link
Member

Choose a reason for hiding this comment

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

I assume it's better to throw UncheckedIOException instead?
At least, we cannot ignore this exception like this, because we surely have problem in generated SARIF report file.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

Copy link
Member

Choose a reason for hiding this comment

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

Here we do not need to invoke e.printStackTrace(System.err);. Who catches the error will print it.

And when we need to log, we use SLF4J as a logging facade so please do not use System.err directly.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed printStackTrace to let caller to log the error.

Comment on lines 37 to 54
try {
Gson gson = new Gson();
jsonWriter.name("runs").beginArray().beginObject();
BugCollectionAnalyser analyser = new BugCollectionAnalyser(getBugCollection());
processTool(jsonWriter, analyser.getRules());
processInvocations(jsonWriter, analyser.getBaseToId());

jsonWriter.name("results").beginArray();
analyser.getResults().forEach((result) -> gson.toJson(result, jsonWriter));
jsonWriter.endArray();

jsonWriter.name("originalUriBaseIds");
gson.toJson(analyser.getOriginalUriBaseIds(), jsonWriter);

jsonWriter.endObject().endArray(); // end "runs", end "runs" array
} catch (IOException e) {
throw e;
}
Copy link
Member

Choose a reason for hiding this comment

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

This try-block seems needless.

Suggested change
try {
Gson gson = new Gson();
jsonWriter.name("runs").beginArray().beginObject();
BugCollectionAnalyser analyser = new BugCollectionAnalyser(getBugCollection());
processTool(jsonWriter, analyser.getRules());
processInvocations(jsonWriter, analyser.getBaseToId());
jsonWriter.name("results").beginArray();
analyser.getResults().forEach((result) -> gson.toJson(result, jsonWriter));
jsonWriter.endArray();
jsonWriter.name("originalUriBaseIds");
gson.toJson(analyser.getOriginalUriBaseIds(), jsonWriter);
jsonWriter.endObject().endArray(); // end "runs", end "runs" array
} catch (IOException e) {
throw e;
}
Gson gson = new Gson();
jsonWriter.name("runs").beginArray().beginObject();
BugCollectionAnalyser analyser = new BugCollectionAnalyser(getBugCollection());
processTool(jsonWriter, analyser.getRules());
processInvocations(jsonWriter, analyser.getBaseToId());
jsonWriter.name("results").beginArray();
analyser.getResults().forEach((result) -> gson.toJson(result, jsonWriter));
jsonWriter.endArray();
jsonWriter.name("originalUriBaseIds");
gson.toJson(analyser.getOriginalUriBaseIds(), jsonWriter);
jsonWriter.endObject().endArray(); // end "runs", end "runs" array

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Edit: fixed.
Most methods of Gson JsonWriter like name/value/begin.../end... throw IOException. That's why I have to catch it in try catch block.

Comment on lines 77 to 84
try {
Gson gson = new Gson();
jsonWriter.name("invocations").beginArray();
gson.toJson(invocation.toJsonObject(), jsonWriter);
jsonWriter.endArray();
} catch (IOException e) {
throw e;
}
Copy link
Member

Choose a reason for hiding this comment

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

This try-block seems needless.

Suggested change
try {
Gson gson = new Gson();
jsonWriter.name("invocations").beginArray();
gson.toJson(invocation.toJsonObject(), jsonWriter);
jsonWriter.endArray();
} catch (IOException e) {
throw e;
}
Gson gson = new Gson();
jsonWriter.name("invocations").beginArray();
gson.toJson(invocation.toJsonObject(), jsonWriter);
jsonWriter.endArray();

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

jsonWriter.key("rules").value(rules);
jsonWriter.endObject().endObject();
private void processTool(@NonNull JsonWriter jsonWriter, @NonNull JsonArray rules) throws IOException {
try {
Copy link
Member

Choose a reason for hiding this comment

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

This try-block is also needless. Simply throw IOException to the caller side.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

jsonWriter.endObject().endObject();
private void processTool(@NonNull JsonWriter jsonWriter, @NonNull JsonArray rules) throws IOException {
try {
Gson gson = new Gson();
Copy link
Member

Choose a reason for hiding this comment

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

Is it OK to generate Gson instance on demand? Not sure its cost, but if it takes much resources (CPU or Java heap), consider to create it once and reuse by providing it via method parameter.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added a Gson member instead creating new instance for each place.
According to Gson user guide:

The Gson instance does not maintain any state while invoking Json operations. So, you are free to reuse the same object for multiple Json serialization and deserialization operations.

rules.forEach((rule) -> gson.toJson(rule, jsonWriter));
jsonWriter.endArray();

jsonWriter.endObject().endObject(); // end "driver", end "tool"
Copy link
Member

Choose a reason for hiding this comment

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

Good comment to keep the code readable! 👍

DetectorFactoryCollection.instance().plugins().stream().map(Extension::fromPlugin).map(Extension::toJSONObject).forEach(jsonWriter::value);
jsonWriter.endArray();
private void processExtensions(@NonNull JsonWriter jsonWriter) throws IOException {
try {
Copy link
Member

Choose a reason for hiding this comment

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

this try-block seems needless.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

@yongyan-gh yongyan-gh marked this pull request as ready for review February 22, 2021 18:24
Copy link
Member

@KengoTODA KengoTODA left a comment

Choose a reason for hiding this comment

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

Cool. Just one change is necessary, please check.

"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json");
processRuns(jsonWriter);
jsonWriter.endObject();
getBugCollection().bugsPopulated();
} catch (IOException e) {
e.printStackTrace(System.err);
Copy link
Member

Choose a reason for hiding this comment

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

Here we do not need to invoke e.printStackTrace(System.err);. Who catches the error will print it.

And when we need to log, we use SLF4J as a logging facade so please do not use System.err directly.

Copy link
Member

@KengoTODA KengoTODA left a comment

Choose a reason for hiding this comment

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

LGTM. Thanks for your contribution!

@KengoTODA
Copy link
Member

@spotbugs/everyone if possible, please double-check this PR. I'm recently little bit careless to make a high quality review. 🙇‍♂️

Copy link
Contributor Author

@yongyan-gh yongyan-gh left a comment

Choose a reason for hiding this comment

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

LGTM. Thanks for your contribution!

Thank you for reviewing!

Comment on lines 37 to 54
try {
Gson gson = new Gson();
jsonWriter.name("runs").beginArray().beginObject();
BugCollectionAnalyser analyser = new BugCollectionAnalyser(getBugCollection());
processTool(jsonWriter, analyser.getRules());
processInvocations(jsonWriter, analyser.getBaseToId());

jsonWriter.name("results").beginArray();
analyser.getResults().forEach((result) -> gson.toJson(result, jsonWriter));
jsonWriter.endArray();

jsonWriter.name("originalUriBaseIds");
gson.toJson(analyser.getOriginalUriBaseIds(), jsonWriter);

jsonWriter.endObject().endArray(); // end "runs", end "runs" array
} catch (IOException e) {
throw e;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Edit: fixed.
Most methods of Gson JsonWriter like name/value/begin.../end... throw IOException. That's why I have to catch it in try catch block.

extensionJson.addProperty("informationUri", informationUri.toString());
}
if (organization != null) {
extensionJson.addProperty("organization", String.valueOf(organization));
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.
organization was declared as Object it should be String

"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json");
processRuns(jsonWriter);
jsonWriter.endObject();
getBugCollection().bugsPopulated();
} catch (IOException e) {
e.printStackTrace(System.err);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

jsonWriter.endObject().endObject();
private void processTool(@NonNull JsonWriter jsonWriter, @NonNull JsonArray rules) throws IOException {
try {
Gson gson = new Gson();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added a Gson member instead creating new instance for each place.
According to Gson user guide:

The Gson instance does not maintain any state while invoking Json operations. So, you are free to reuse the same object for multiple Json serialization and deserialization operations.

public String toJSONString() {
return String.format("\"%s\"", name().toLowerCase());
public String toJsonString() {
return name().toLowerCase();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added SerializedName annotation to the enum. Its useful if we use Gson toJson/fromJson methods to serialize/deserialize objects.

Current we create/setup JsonObject instead serializing object. e.g.

        JsonObject result = new JsonObject();
        result.addProperty("ruleId", ruleId);
        result.addProperty("ruleIndex", ruleIndex);;
        result.addProperty("level", level.toJsonString());

I think after POJO classes are introduced we should be able to use object serialization/deserialization and not need this method anymore

public String toJSONString() {
return String.format("\"%s\"", name().toLowerCase());
public String toJsonString() {
return name().toLowerCase();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Also tried
return new Gson().toJson(this);
but it returned string with double quotes "\"warning"\", but we need string without double quotes "warning"

Comment on lines 77 to 84
try {
Gson gson = new Gson();
jsonWriter.name("invocations").beginArray();
gson.toJson(invocation.toJsonObject(), jsonWriter);
jsonWriter.endArray();
} catch (IOException e) {
throw e;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

jsonWriter.key("rules").value(rules);
jsonWriter.endObject().endObject();
private void processTool(@NonNull JsonWriter jsonWriter, @NonNull JsonArray rules) throws IOException {
try {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

DetectorFactoryCollection.instance().plugins().stream().map(Extension::fromPlugin).map(Extension::toJSONObject).forEach(jsonWriter::value);
jsonWriter.endArray();
private void processExtensions(@NonNull JsonWriter jsonWriter) throws IOException {
try {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json");
processRuns(jsonWriter);
jsonWriter.endObject();
getBugCollection().bugsPopulated();
} catch (IOException e) {
e.printStackTrace(System.err);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed printStackTrace to let caller to log the error.

@yongyan-gh
Copy link
Contributor Author

Attached the Sarif log generated by SpotBugs with the Gson changes.
Except some name/value pairs are in different order, its identical to original Sarif log file generated using org.json
gsonResult.zip

@yongyan-gh
Copy link
Contributor Author

@KengoTODA
I ran all tests all passed and adhoc tests it works as expected. Can you pls merge this change if no more issue?
Also pls review #1434.

Thanks!

@KengoTODA KengoTODA merged commit 41485ae into spotbugs:master Mar 3, 2021
@michaelcfanning
Copy link

Fantastic!

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

Successfully merging this pull request may close these issues.

None yet

3 participants