Skip to content
This repository was archived by the owner on May 12, 2021. It is now read-only.

less memory consumption in json deserializer #9

Open
wants to merge 1 commit into
base: develop
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
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,13 @@ private static class StringAdapter extends ToJsonStringAdapter<String>
@Override
public String deserialize( JsonValue json, BiFunction<JsonValue, ValueType, Object> deserialize )
{
return JavaxJson.asString( json );
switch (json.getValueType()) {
case NULL:
return null;
default:
return JavaxJson.asString(json);
}

}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,7 @@
*/
package org.apache.polygene.serialization.javaxjson;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.io.UncheckedIOException;
import java.lang.reflect.Array;
import java.util.AbstractMap;
import java.util.ArrayList;
Expand All @@ -31,17 +27,15 @@
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Stream;
import javax.json.JsonArray;
import javax.json.JsonObject;
import javax.json.JsonReader;
import javax.json.JsonString;
import javax.json.JsonStructure;
import javax.json.JsonValue;
import javax.json.stream.JsonParser;
import javax.json.stream.JsonParsingException;
import org.apache.polygene.api.association.AssociationDescriptor;
import org.apache.polygene.api.composite.CompositeDescriptor;
import org.apache.polygene.api.composite.StatefulAssociationCompositeDescriptor;
Expand All @@ -68,7 +62,6 @@
import static java.util.Collections.unmodifiableList;
import static java.util.Collections.unmodifiableMap;
import static java.util.Collections.unmodifiableSet;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toCollection;
import static org.apache.polygene.api.util.Collectors.toMapWithNullValues;
import static org.apache.polygene.serialization.javaxjson.JavaxJson.asString;
Expand All @@ -92,88 +85,35 @@ public class JavaxJsonDeserializer extends AbstractTextDeserializer
private ServiceDescriptor descriptor;

private JavaxJsonSettings settings;
private JsonString emptyJsonString;

@Override
public void initialize() throws Exception
{
settings = JavaxJsonSettings.orDefault( descriptor.metaInfo( JavaxJsonSettings.class ) );
emptyJsonString = jsonFactories.builderFactory().createObjectBuilder().add( "s", "" ).build()
.getJsonString( "s" );
}

@Override
public <T> T deserialize( ModuleDescriptor module, ValueType valueType, Reader state )
@SuppressWarnings("unchecked")
public <T> T deserialize(ModuleDescriptor module, ValueType valueType, Reader state)
{
// JSR-353 Does not allow reading "out of structure" values
// See https://www.jcp.org/en/jsr/detail?id=353
// And commented JsonReader#readValue() method in the javax.json API
// BUT, it will be part of the JsonReader contract in the next version
// See https://www.jcp.org/en/jsr/detail?id=374
// Implementation by provider is optional though, so we'll always need a default implementation here.
// Fortunately, JsonParser has new methods allowing to read structures while parsing so it will be easy to do.
// In the meantime, a poor man's implementation reading the json into memory will do.
// TODO Revisit values out of structure JSON deserialization when JSR-374 is out
String stateString;
try( BufferedReader buffer = new BufferedReader( state ) )
{
stateString = buffer.lines().collect( joining( "\n" ) );
}
catch( IOException ex )
{
throw new UncheckedIOException( ex );
}
// We want plain Strings, BigDecimals, BigIntegers to be deserialized even when unquoted
Function<String, T> plainValueFunction = string ->
{
String poorMans = "{\"value\":" + string + "}";
JsonObject poorMansJson = jsonFactories.readerFactory()
.createReader( new StringReader( poorMans ) )
.readObject();
JsonValue value = poorMansJson.get( "value" );
return fromJson( module, valueType, value );
};
Function<String, T> outOfStructureFunction = string ->
Converter<Object> converter = converters.converterFor(valueType);
if (converter != null)
{
// Is this an unquoted plain value?
try
{
return plainValueFunction.apply( '"' + string + '"' );
}
catch( JsonParsingException ex )
{
return plainValueFunction.apply( string );
}
};
try( JsonParser parser = jsonFactories.parserFactory().createParser( new StringReader( stateString ) ) )
{
if( parser.hasNext() )
{
JsonParser.Event e = parser.next();
switch( e )
{
case VALUE_NULL:
return null;
case START_ARRAY:
case START_OBJECT:
// JSON Structure
try( JsonReader reader = jsonFactories.readerFactory()
.createReader( new StringReader( stateString ) ) )
{
return fromJson( module, valueType, reader.read() );
}
default:
// JSON Value out of structure
return outOfStructureFunction.apply( stateString );
}
String stateString = readString(state);
if (isJsonNull(stateString)) {
return null;
} else {
return (T) converter.fromString(stateString);
}
}
catch( JsonParsingException ex )
{
return outOfStructureFunction.apply( stateString );

JavaxJsonAdapter<?> adapter = adapters.adapterFor(valueType);
if (adapter != null) {
return (T) adapter.deserialize(readJsonString(state), (jsonValue, type) -> doDeserialize(module, type, jsonValue));
}

try (JsonReader reader = jsonFactories.readerFactory().createReader(state)) {
return fromJson(module, valueType, reader.readValue());
}
// Empty state string?
return fromJson( module, valueType, emptyJsonString );
}

@Override
Expand All @@ -182,6 +122,24 @@ public <T> T fromJson( ModuleDescriptor module, ValueType valueType, JsonValue s
return doDeserialize( module, valueType, state );
}

private JsonValue readJsonString(Reader reader) {
String str = readString(reader);
if (isJsonNull(str)) {
return JsonValue.NULL;
} else {
return jsonFactories.provider().createValue(str);
}
}

private boolean isJsonNull(String str) {
return "null".equals(str);
}

private String readString(Reader reader) {
Scanner scanner = new Scanner(reader).useDelimiter("\\A");
return scanner.hasNext() ? scanner.next() : "";
}

@SuppressWarnings( "unchecked" )
private <T> T doDeserialize( ModuleDescriptor module, ValueType valueType, JsonValue json )
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ public interface JavaxJsonFactories

JsonWriterFactory writerFactory();

JsonProvider provider();
/**
* Creates a {@link JsonString} with the {@link Object#toString()} result on the given object.
*
Expand Down Expand Up @@ -111,14 +112,14 @@ class Mixin implements JavaxJsonFactories, Initializable
private JsonGeneratorFactory generatorFactory;
private JsonBuilderFactory builderFactory;
private JsonWriterFactory writerFactory;
private JsonProvider jsonProvider;

@Override
public void initialize() throws Exception
{
JavaxJsonSettings settings = JavaxJsonSettings.orDefault( descriptor.metaInfo( JavaxJsonSettings.class ) );

String jsonProviderClassName = settings.getJsonProviderClassName();
JsonProvider jsonProvider;
if( jsonProviderClassName == null )
{
jsonProvider = JsonProvider.provider();
Expand Down Expand Up @@ -251,5 +252,10 @@ public JsonArrayBuilder cloneBuilderExclude( JsonArray jsonArray, JsonValue... v
}
return job;
}

@Override
public JsonProvider provider() {
return jsonProvider;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import javax.json.JsonObjectBuilder;
import javax.json.JsonString;
import javax.json.JsonValue;
import javax.json.JsonWriter;
import org.apache.polygene.api.PolygeneAPI;
import org.apache.polygene.api.association.AssociationStateHolder;
import org.apache.polygene.api.common.Optional;
Expand Down Expand Up @@ -98,7 +99,9 @@ public void serialize( Options options, Writer writer, @Optional Object object )
}
else
{
writer.write( jsonValue.toString() );
try (JsonWriter w = jsonFactories.writerFactory().createWriter(writer)) {
w.write(jsonValue);
}
}
}
catch( IOException ex )
Expand Down
2 changes: 1 addition & 1 deletion dependencies.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def jcloudsVersion = '2.0.2'
def jdbmVersion = '2.4'
def jedisVersion = '2.9.0'
def jettyVersion = '9.2.17.v20160517' // 9.3.x Tests fail!
def johnzonVersion = '1.1.1'
def johnzonVersion = '1.1.7'
def jooqVersion = '3.10.6'
def kotlinVersion = '1.2.31'
def leveldbVersion = '0.9'
Expand Down