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

Add support for JsonGenerator.Key #47

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions impl/src/main/java/org/eclipse/parsson/JsonGeneratorImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,18 @@ private JsonGenerator writeName(String name) {
return this;
}

private void writeName(Key name) {
writeComma();
final char[] escaped = name.toCharArray();
Copy link
Author

Choose a reason for hiding this comment

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

nb: char[] escaped includes the quotes

final int required = escaped.length;
if (len + required >= buf.length) {
flushBuffer();
}
System.arraycopy(escaped, 0, buf, len, required);
len += required;
writeColon();
}

@Override
public JsonGenerator write(String name, String fieldValue) {
write(name, (CharSequence) fieldValue);
Expand Down Expand Up @@ -464,6 +476,19 @@ public JsonGenerator writeKey(String name) {
return this;
}

@Override
public JsonGenerator writeKey(JsonGenerator.Key name) {
if (currentContext.scope != Scope.IN_OBJECT) {
throw new JsonGenerationException(
JsonMessages.GENERATOR_ILLEGAL_METHOD(currentContext.scope));
}
writeName(name);
stack.push(currentContext);
Copy link
Author

Choose a reason for hiding this comment

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

Could refactor extract these 4 lines as they are the same as the lines from writeKey(String name)

currentContext = new Context(Scope.IN_FIELD);
currentContext.first = false;
return this;
}

@Override
public JsonGenerator writeEnd() {
if (currentContext.scope == Scope.IN_NONE) {
Expand Down
103 changes: 103 additions & 0 deletions impl/src/main/java/org/eclipse/parsson/JsonGeneratorKeyImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
* Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/

package org.eclipse.parsson;

import jakarta.json.stream.JsonGenerator;

final class JsonGeneratorKeyImpl {

static JsonGenerator.Key createKey(String name) {
return new Key(quoteEscape(name));
}

/**
* A key that is already escaped so then can just be written directly.
*/
private static class Key implements JsonGenerator.Key {

private final char[] escapedName;

Key(String escapedName) {
this.escapedName = escapedName.toCharArray();
}

@Override
public char[] toCharArray() {
return escapedName;
}
}

private static String quoteEscape(CharSequence string) {
StringBuilder builder = new StringBuilder();
Copy link
Author

Choose a reason for hiding this comment

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

Same escape logic as JsonGeneratorImpl.writeEscapedString() but writes to the StringBuilder instead

builder.append('"');
int len = string.length();
for (int i = 0; i < len; i++) {
int begin = i, end = i;
char c = string.charAt(i);
// find all the characters that need not be escaped
// unescaped = %x20-21 | %x23-5B | %x5D-10FFFF
while (c >= 0x20 && c <= 0x10ffff && c != 0x22 && c != 0x5c) {
i++;
end = i;
if (i < len) {
c = string.charAt(i);
} else {
break;
}
}
// Write characters without escaping
if (begin < end) {
builder.append(string, begin, end);
if (i == len) {
break;
}
}

switch (c) {
case '"':
case '\\':
builder.append('\\');
builder.append(c);
break;
case '\b':
builder.append('\\');
builder.append('b');
break;
case '\f':
builder.append('\\');
builder.append('f');
break;
case '\n':
builder.append('\\');
builder.append('n');
break;
case '\r':
builder.append('\\');
builder.append('r');
break;
case '\t':
builder.append('\\');
builder.append('t');
break;
default:
String hex = "000" + Integer.toHexString(c);
builder.append("\\u" + hex.substring(hex.length() - 4));
}
}
return builder.append('"').toString();
}
}
5 changes: 5 additions & 0 deletions impl/src/main/java/org/eclipse/parsson/JsonProviderImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@
public class JsonProviderImpl extends JsonProvider {
private final BufferPool bufferPool = new BufferPoolImpl();

@Override
public JsonGenerator.Key createGeneratorKey(String name) {
return JsonGeneratorKeyImpl.createKey(name);
}

@Override
public JsonGenerator createGenerator(Writer writer) {
return new JsonGeneratorImpl(writer, bufferPool);
Expand Down
43 changes: 41 additions & 2 deletions impl/src/test/java/org/eclipse/parsson/tests/JsonFieldTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,12 @@

package org.eclipse.parsson.tests;

import jakarta.json.spi.JsonProvider;
import junit.framework.TestCase;

import jakarta.json.Json;
import jakarta.json.JsonBuilderFactory;
import jakarta.json.JsonObject;
import jakarta.json.JsonObjectBuilder;
import jakarta.json.JsonValue;
import jakarta.json.stream.JsonGenerationException;
import jakarta.json.stream.JsonGenerator;
import java.io.StringWriter;
Expand All @@ -37,6 +36,46 @@
*/
public class JsonFieldTest extends TestCase {

public void testKeyFieldObject() {
StringWriter sw = new StringWriter();
JsonProvider provider = JsonProvider.provider();

JsonGenerator.Key f1Name = provider.createGeneratorKey("f1Name");
JsonGenerator.Key innerFieldName = provider.createGeneratorKey("innerFieldName");
JsonGenerator.Key f2Name = provider.createGeneratorKey("f2Name");

JsonGenerator generator = provider.createGenerator(sw);

generator.writeStartObject();
generator.writeKey(f1Name);
generator.writeStartObject();
generator.writeKey(innerFieldName).write("innerFieldValue");
generator.writeEnd();
generator.writeKey(f2Name).write("f2Value");
generator.writeEnd();

generator.close();
assertEquals("{\"f1Name\":{\"innerFieldName\":\"innerFieldValue\"},\"f2Name\":\"f2Value\"}", sw.toString());
}

public void testKeyFieldEscaped() {
StringWriter sw = new StringWriter();
JsonProvider provider = JsonProvider.provider();

JsonGenerator.Key f1Name = provider.createGeneratorKey("\"f1Name\"");
JsonGenerator.Key f2Name = provider.createGeneratorKey("f2-name");

JsonGenerator generator = provider.createGenerator(sw);

generator.writeStartObject();
generator.writeKey(f1Name).write("a");
generator.writeKey(f2Name).write("b");
generator.writeEnd();

generator.close();
assertEquals("{\"\\\"f1Name\\\"\":\"a\",\"f2-name\":\"b\"}", sw.toString());
}

public void testFieldAsOnlyMember() {
StringWriter sw = new StringWriter();
JsonGenerator generator = Json.createGenerator(sw);
Expand Down