Cut too long strings in json using gson - java

I would like to cut too long strings in json.
In order to do that I would like to register new type adapter for String type and inside this deserializer I will check and limit too long strings.
Gson gson = new GsonBuilder().registerTypeAdapter(String.class, new CuttingStringDeserializer()).create();
JsonElement element = gson.fromJson(jsonString, JsonElement.class);
return new GsonBuilder().setPrettyPrinting().create().toJson(element);
Example of json file that I want to process:
{
"myString": "this string is too long - cut it",
"other": "this is ok"
}
Desired output:
{
"myString": "this strin",
"other": "this is ok"
}
In general I don't know structure of json but I want to filter all string occurrences.
Deserializer:
public class CuttingStringDeserializer implements JsonDeserializer<String> {
#Override
public String deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context)
throws JsonParseException {
String s = json.getAsString();
if(s.lenght() > MAX_CONTENT_LENGTH){
return s.substring(0, MAX_CONTENT_LENGTH);
}else{
return s;
}
}
Unfortunately my custom deserializer is not called by gson.

This (using some custom JsonWriter) works:
package so41793888;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.stream.JsonWriter;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import java.io.IOException;
import java.io.StringWriter;
public class Demo {
public static void main(String[] args) {
String jsonString = "{\n" +
" \"myString\": \"this string is too long - cut it\",\n" +
" \"other\": \"this is ok\"\n" +
"}";
Gson gson = new GsonBuilder().create();
JsonElement element = gson.fromJson(jsonString, JsonElement.class);
StringWriter out = null;
try {
out = new StringWriter();
new GsonBuilder().create().toJson(element, new MyJsonWriter(out));
System.out.println(out.getBuffer().toString());
} finally {
IOUtils.closeQuietly(out);
}
}
private static class MyJsonWriter extends JsonWriter {
public MyJsonWriter(final StringWriter out) {
super(out);
setIndent(" ");
}
#Override
public JsonWriter value(final String value) throws IOException {
return super.value(StringUtils.abbreviate(value, 12));
}
}
}
outputs:
{
"myString": "this stri...",
"other": "this is ok"
}

You can reject the idea of tree processing (the way how JsonSerializer and JsonDeserializer work) in favor of stream processing, where you analyze every token on your own. GsonBuilder seems not to allow overriding a streaming-fashioned TypeAdapters as well, but you can then use JsonReader in order to parse every token from an input stream, and JsonWriter to emit processed tokens to an output stream. This may look too low level, but since it's a streaming way, it is really cheap and does not consume much memory as tree processing usually does. Thus you can process even infinite streams.
#SuppressWarnings("resource")
private static void trim(final int maxStringLength, final Reader reader, final Writer writer)
throws IOException {
// a specifically configured IDEA complains for the unclosed jsonReader, but invoking the `close` method is a like a chain and sometimes undesirable
#SuppressWarnings("all")
final JsonReader jsonReader = new JsonReader(reader);
// the same goes to jsonWriter
#SuppressWarnings("all")
final JsonWriter jsonWriter = new JsonWriter(writer);
for ( JsonToken token; (token = jsonReader.peek()) != END_DOCUMENT; ) {
switch ( token ) {
case BEGIN_ARRAY:
// merely reflect a BEGIN_ARRAY token
jsonReader.beginArray();
jsonWriter.beginArray();
break;
case END_ARRAY:
// merely reflect an END_ARRAY token
jsonReader.endArray();
jsonWriter.endArray();
break;
case BEGIN_OBJECT:
// merely reflect a BEGIN_OBJECT token
jsonReader.beginObject();
jsonWriter.beginObject();
break;
case END_OBJECT:
// merely reflect an END_OBJECT token
jsonReader.endObject();
jsonWriter.endObject();
break;
case NAME:
// merely reflect NAME tokens (or trim?)
jsonWriter.name(jsonReader.nextName());
break;
case STRING:
// trimming a STRING token if necessary
final String string = jsonReader.nextString();
jsonWriter.value(string.length() > maxStringLength ? string.substring(0, maxStringLength) : string);
break;
case NUMBER:
// NUMBER tokens are a bit complex because JSON only denotes a double number that can be literally an integer
final String rawNumber = jsonReader.nextString();
try {
// try to write the biggest integer number supported by Java, floating points also fail to be parsed as long values
jsonWriter.value(parseLong(rawNumber));
} catch ( final NumberFormatException nex1 ) {
try {
// not a long value, then perhaps it's a double value?
jsonWriter.value(parseDouble(rawNumber));
} catch ( final NumberFormatException nex2 ) {
// can't think of specific cases here...
throw new AssertionError("Must not happen", nex2);
}
}
break;
case BOOLEAN:
// merely reflect BOOLEAN tokens
jsonWriter.value(jsonReader.nextBoolean());
break;
case NULL:
// merely reflect NULL tokens
jsonReader.nextNull();
jsonWriter.nullValue();
break;
case END_DOCUMENT:
// fall through, because this type of tokens is checked above, and it's fine to throw an assertion error
default:
throw new AssertionError(token);
}
}
}
This method, of course, does not support pretty printing, but it can be easily implemented if it's really necessary.
And how it's used:
final Reader reader = new StringReader("{\"myString\":\"this string is too long - cut it\",\"other\":\"this is ok\"}");
final Writer writer = new OutputStreamWriter(System.out); // redirect the output to System.out
trim(10, reader, writer);
writer.flush(); // flushing at a call-site, we decide
The output:
{"myString":"this strin","other":"this is ok"}
The solution can work with any kind of JSON, having no background for a particular type. Simply speaking, it's just type-unaware and can process even simple single literals like "foo-bar-baz-qux".

It seems for me it doesn't make sense what are you trying to archive, but here kick off code which should help .
public class Main {
private static String json = "{\"myString\": \"this string is too long - limit it\",\"other\": \"this is ok\"}";
public static void main(String... var) {
System.out.print(cutJson(json));
}
public static String cutJson(String json) {
Type type = new TypeToken<Map<String, String>>() {
}.getType();
Gson gson = new GsonBuilder().registerTypeAdapter(type, new CuttingStringDeserializer()).create();
Map<String, String> element = gson.fromJson(json, type);
return new GsonBuilder().setPrettyPrinting().create().toJson(element);
}
private static class CuttingStringDeserializer implements JsonDeserializer<Map<String, String>> {
#Override
public Map<String, String> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
Iterator<Map.Entry<String, JsonElement>> iterator = ((JsonObject) json).entrySet().iterator();
Map<String, String> result = new HashMap<String, String>();
while (iterator.hasNext()) {
Map.Entry<String, JsonElement> entry = iterator.next();
if (entry.getValue().getAsString().length() > 10) {
entry.setValue(new JsonPrimitive(entry.getValue().getAsString().substring(0, 9)));
}
result.put(entry.getKey(), entry.getValue().getAsString());
}
return result;
}
}
}
Prints:
{
"myString": "this stri",
"other": "this is ok"
}

Related

Gson Custom TypeAdapter for List Not Called

I am new to using Gson. I am trying to create a specific typeadapter for my class. Here it is:
public class AssetSerializer extends TypeAdapter<List<Asset>> {
#Override
public void write(JsonWriter out, List<Asset> value) throws IOException {
out.beginArray();
for (Asset asset : value) {
out.beginObject();
out.name("name").value(asset.getName());
out.name("code").value(asset.getCode());
out.endObject();
}
out.endArray();
}
#Override
public List<Asset> read(JsonReader in) throws IOException {
String temp_name = "";
String temp_code = "";
List<Asset> list = new LinkedList<>();
in.beginArray();
while (in.hasNext()) {
switch (in.peek()) {
case BEGIN_OBJECT:
in.beginObject();
while (in.hasNext()) {
switch (in.nextName()) {
case "name":
temp_name = in.nextString();
continue;
case "code":
temp_code = in.nextString();
continue;
}
}
in.endObject();
Asset temp_asset = new Asset(temp_name, temp_code);
list.add(temp_asset);
continue;
}
}
in.endArray();
return list;
}
This is the way I am trying to serialize/deserialize a list of Assets:
Asset asset1 = new Asset("Asset1", "code_1");
Asset asset2 = new Asset("Asset2", "code_2");
LinkedList<Asset> temp_list = new LinkedList<Asset>();
temp_list.add(asset1);
temp_list.add(asset2);
Type assetsType = new TypeToken<List<Asset>>(){}.getType();
Gson gson = new GsonBuilder()
.setPrettyPrinting()
.registerTypeAdapter(assetsType, new AssetSerializer())
.create();
String json = gson.toJson(temp_list);
The problem is that my overridden methods are not called in this code, so Gson uses its standard serializer for arrays.
You need to register your custom adapter with GsonBuilder to solve the problem.

javax.json serializer/deserializer for Gson

I'm trying to write a general Gson serializer/deserializer for java.javax.JsonObjects:
public static class JavaxJsonObjConverter implements JsonSerializer<JsonObject>, JsonDeserializer<JsonObject> {
#Override
public JsonObject deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
return JsonUtils.getJsonObjectFromString(json.toString());
}
#Override
public JsonElement serialize(JsonObject src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonParser().parse(src.toString());
}
}
When I try to serialize a java.json.JsonObject, I get this error:
Exception in thread "main" java.lang.ClassCastException: org.glassfish.json.JsonStringImpl cannot be cast to javax.json.JsonObject
at om.headlandstech.utils.gson_utils.GsonUtils$JavaxJsonValueConverter.serialize(>GsonUtils.java:1)
at com.google.gson.internal.bind.TreeTypeAdapter.write(TreeTypeAdapter.java:81)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:69)
at com.google.gson.internal.bind.MapTypeAdapterFactory$Adapter.write(MapTypeAdapte>rFactory.java:208)
at ....
It would be much better if you'd post the javax.json.JsonObject instance as well (they way it's built). Because: the closest I can reproduce it with is the following:
final Gson gson = new GsonBuilder()
.registerTypeAdapter(JsonObject.class, new JavaxJsonObjConverter())
.create();
final JsonObject src = Json.createObjectBuilder()
.add("foo", "bar")
.build();
System.out.println(gson.toJson(src.get("foo"), JsonObject.class));
Exception:
Exception in thread "main" java.lang.ClassCastException: org.glassfish.json.JsonStringImpl cannot be cast to javax.json.JsonObject
at q43376802.Q43376802$JavaxJsonObjConverter.serialize(Q43376802.java:29)
at com.google.gson.internal.bind.TreeTypeAdapter.write(TreeTypeAdapter.java:81)
at com.google.gson.Gson.toJson(Gson.java:669)
at com.google.gson.Gson.toJson(Gson.java:648)
at com.google.gson.Gson.toJson(Gson.java:603)
at q43376802.Q43376802.main(Q43376802.java:26)
Next thing. Your JavaxJsonObjConverter implements a javax.json.JavaObject (de)serializer, but javax.json.JavaObject is not the root of JSON objects in javax.json. The hierarchy root is JsonValue. So your (de)serializer must deal with JsonValue rather than JsonObject.
public static class JavaxJsonValConverter
implements JsonSerializer<JsonValue> {
#Override
public JsonElement serialize(final JsonValue jsonValue, final Type type, final JsonSerializationContext context) {
return new JsonParser().parse(jsonValue.toString());
}
}
And register it with removing and deleting JavaxJsonObjConverter entirely:
.registerTypeAdapter(JsonValue.class, new JavaxJsonValConverter())
However, the serializer above is naive and requires more resources however, giving you some flexibility (when reading/writing directly from/to JSON streams may be too unjustified (compare DOM and SAX in XML -- it's the same story)):
JsonSerializer and JsonDeserializer rely on JSON tree model representation that's implemented with JsonElement. This means that entire JSON has to be loaded into memory and its tree model has to be built before you can use it. This would consume much more memory if JSON objects you're going to deal with are large.
toString() is a bad choice either: it requires internal strings to be generated first, thus consuming memory again.
So, the items above may make a really large memory print. In order to save memory resources, you can create a Gson TypeAdapter that can work with JSON streams (and that is the base for every (de)serializer in JSON).
final class JsonValueTypeAdapter
extends TypeAdapter<JsonValue> {
private static final TypeAdapter<JsonValue> jsonValueTypeAdapter = new JsonValueTypeAdapter();
private JsonValueTypeAdapter() {
}
static TypeAdapter<JsonValue> getJsonValueTypeAdapter() {
return jsonValueTypeAdapter;
}
#Override
public void write(final JsonWriter out, final JsonValue jsonValue)
throws IOException {
final ValueType valueType = jsonValue.getValueType();
switch ( valueType ) {
case ARRAY:
JsonArrayTypeAdapter.instance.write(out, (JsonArray) jsonValue);
break;
case OBJECT:
JsonObjectTypeAdapter.instance.write(out, (JsonObject) jsonValue);
break;
case STRING:
JsonStringTypeAdapter.instance.write(out, (JsonString) jsonValue);
break;
case NUMBER:
JsonNumberTypeAdapter.instance.write(out, (JsonNumber) jsonValue);
break;
case TRUE:
JsonBooleanTypeAdapter.instance.write(out, jsonValue);
break;
case FALSE:
JsonBooleanTypeAdapter.instance.write(out, jsonValue);
break;
case NULL:
JsonNullTypeAdapter.instance.write(out, jsonValue);
break;
default:
throw new AssertionError(valueType);
}
}
#Override
public JsonValue read(final JsonReader in)
throws IOException {
final JsonToken jsonToken = in.peek();
switch ( jsonToken ) {
case BEGIN_ARRAY:
return JsonArrayTypeAdapter.instance.read(in);
case END_ARRAY:
throw new AssertionError("Must never happen due to delegation to the array type adapter");
case BEGIN_OBJECT:
return JsonObjectTypeAdapter.instance.read(in);
case END_OBJECT:
throw new AssertionError("Must never happen due to delegation to the object type adapter");
case NAME:
throw new AssertionError("Must never happen");
case STRING:
return JsonStringTypeAdapter.instance.read(in);
case NUMBER:
return JsonNumberTypeAdapter.instance.read(in);
case BOOLEAN:
return JsonBooleanTypeAdapter.instance.read(in);
case NULL:
return JsonNullTypeAdapter.instance.read(in);
case END_DOCUMENT:
throw new AssertionError("Must never happen");
default:
throw new AssertionError(jsonToken);
}
}
private static final class JsonNullTypeAdapter
extends TypeAdapter<JsonValue> {
private static final TypeAdapter<JsonValue> instance = new JsonNullTypeAdapter().nullSafe();
#Override
#SuppressWarnings("resource")
public void write(final JsonWriter out, final JsonValue jsonNull)
throws IOException {
out.nullValue();
}
#Override
public JsonValue read(final JsonReader in)
throws IOException {
in.nextNull();
return JsonValue.NULL;
}
}
private static final class JsonBooleanTypeAdapter
extends TypeAdapter<JsonValue> {
private static final TypeAdapter<JsonValue> instance = new JsonBooleanTypeAdapter().nullSafe();
#Override
#SuppressWarnings("resource")
public void write(final JsonWriter out, final JsonValue jsonBoolean)
throws IllegalArgumentException, IOException {
final ValueType valueType = jsonBoolean.getValueType();
switch ( valueType ) {
case TRUE:
out.value(true);
break;
case FALSE:
out.value(false);
break;
case ARRAY:
case OBJECT:
case STRING:
case NUMBER:
case NULL:
throw new IllegalArgumentException("Not a boolean: " + valueType);
default:
throw new AssertionError(jsonBoolean.getValueType());
}
}
#Override
public JsonValue read(final JsonReader in)
throws IOException {
return in.nextBoolean() ? JsonValue.TRUE : JsonValue.FALSE;
}
}
private static final class JsonNumberTypeAdapter
extends TypeAdapter<JsonNumber> {
private static final TypeAdapter<JsonNumber> instance = new JsonNumberTypeAdapter().nullSafe();
#Override
#SuppressWarnings("resource")
public void write(final JsonWriter out, final JsonNumber jsonNumber)
throws IOException {
if ( jsonNumber.isIntegral() ) {
out.value(jsonNumber.longValue());
} else {
out.value(jsonNumber.doubleValue());
}
}
#Override
public JsonNumber read(final JsonReader in)
throws IOException {
// TODO is there a good way to instantiate a JsonNumber instance?
return (JsonNumber) Json.createArrayBuilder()
.add(in.nextDouble())
.build()
.get(0);
}
}
private static final class JsonStringTypeAdapter
extends TypeAdapter<JsonString> {
private static final TypeAdapter<JsonString> instance = new JsonStringTypeAdapter().nullSafe();
#Override
#SuppressWarnings("resource")
public void write(final JsonWriter out, final JsonString jsonString)
throws IOException {
out.value(jsonString.getString());
}
#Override
public JsonString read(final JsonReader in)
throws IOException {
// TODO is there a good way to instantiate a JsonString instance?
return (JsonString) Json.createArrayBuilder()
.add(in.nextString())
.build()
.get(0);
}
}
private static final class JsonObjectTypeAdapter
extends TypeAdapter<JsonObject> {
private static final TypeAdapter<JsonObject> instance = new JsonObjectTypeAdapter().nullSafe();
#Override
#SuppressWarnings("resource")
public void write(final JsonWriter out, final JsonObject jsonObject)
throws IOException {
out.beginObject();
for ( final Entry<String, JsonValue> e : jsonObject.entrySet() ) {
out.name(e.getKey());
jsonValueTypeAdapter.write(out, e.getValue());
}
out.endObject();
}
#Override
public JsonObject read(final JsonReader in)
throws IOException {
final JsonObjectBuilder jsonObjectBuilder = Json.createObjectBuilder();
in.beginObject();
while ( in.hasNext() ) {
final String key = in.nextName();
final JsonToken token = in.peek();
switch ( token ) {
case BEGIN_ARRAY:
jsonObjectBuilder.add(key, jsonValueTypeAdapter.read(in));
break;
case END_ARRAY:
throw new AssertionError("Must never happen due to delegation to the array type adapter");
case BEGIN_OBJECT:
jsonObjectBuilder.add(key, jsonValueTypeAdapter.read(in));
break;
case END_OBJECT:
throw new AssertionError("Must never happen due to delegation to the object type adapter");
case NAME:
throw new AssertionError("Must never happen");
case STRING:
jsonObjectBuilder.add(key, in.nextString());
break;
case NUMBER:
jsonObjectBuilder.add(key, in.nextDouble());
break;
case BOOLEAN:
jsonObjectBuilder.add(key, in.nextBoolean());
break;
case NULL:
in.nextNull();
jsonObjectBuilder.addNull(key);
break;
case END_DOCUMENT:
// do nothing
break;
default:
throw new AssertionError(token);
}
}
in.endObject();
return jsonObjectBuilder.build();
}
}
private static final class JsonArrayTypeAdapter
extends TypeAdapter<JsonArray> {
private static final TypeAdapter<JsonArray> instance = new JsonArrayTypeAdapter().nullSafe();
#Override
#SuppressWarnings("resource")
public void write(final JsonWriter out, final JsonArray jsonArray)
throws IOException {
out.beginArray();
for ( final JsonValue jsonValue : jsonArray ) {
jsonValueTypeAdapter.write(out, jsonValue);
}
out.endArray();
}
#Override
public JsonArray read(final JsonReader in)
throws IOException {
final JsonArrayBuilder jsonArrayBuilder = Json.createArrayBuilder();
in.beginArray();
while ( in.hasNext() ) {
final JsonToken token = in.peek();
switch ( token ) {
case BEGIN_ARRAY:
jsonArrayBuilder.add(jsonValueTypeAdapter.read(in));
break;
case END_ARRAY:
throw new AssertionError("Must never happen due to delegation to the array type adapter");
case BEGIN_OBJECT:
jsonArrayBuilder.add(jsonValueTypeAdapter.read(in));
break;
case END_OBJECT:
throw new AssertionError("Must never happen due to delegation to the object type adapter");
case NAME:
throw new AssertionError("Must never happen");
case STRING:
jsonArrayBuilder.add(in.nextString());
break;
case NUMBER:
jsonArrayBuilder.add(in.nextDouble());
break;
case BOOLEAN:
jsonArrayBuilder.add(in.nextBoolean());
break;
case NULL:
in.nextNull();
jsonArrayBuilder.addNull();
break;
case END_DOCUMENT:
// do nothing
break;
default:
throw new AssertionError(token);
}
}
in.endArray();
return jsonArrayBuilder.build();
}
}
}
The code above is itself-document I think, despite it's relatively large. Example use:
private static final Gson gson = new GsonBuilder()
.serializeNulls()
.registerTypeHierarchyAdapter(JsonValue.class, getJsonValueTypeAdapter())
.create();
public static void main(final String... args) {
final JsonValue before = createObjectBuilder()
.add("boolean", true)
.add("integer", 3)
.add("string", "foo")
.addNull("null")
.add("array", createArrayBuilder()
.add(false)
.add(2)
.add("bar")
.addNull()
.build())
.build();
System.out.println("before.toString() = " + before);
final String json = gson.toJson(before);
System.out.println("type adapter result = " + json);
final JsonValue after = gson.fromJson(json, JsonValue.class);
System.out.println("after.toString() = " + after);
}
Output:
before.toString() = {"boolean":true,"integer":3,"string":"foo","null":null,"array":[false,2,"bar",null]}
type adapter result = {"boolean":true,"integer":3,"string":"foo","null":null,"array":[false,2,"bar",null]}
after.toString() = {"boolean":true,"integer":3.0,"string":"foo","null":null,"array":[false,2.0,"bar",null]}
Note that the integer property value has been changed: 3 is now 3.0. This happens because JSON does not distinguish between integers, longs, floats, doubles, etc: all it can handle is just a number. You cannot really restore the original number: for example, 3 may be both long and double. The most you can do here is not using .nextDouble() in favor of .nextString() and trying to detect which numeric type it can fit the most and constuct a JsonNumber instance respectively (I'm wondering how it can be done in javax.json -- see the TODO comments in the type adapter).

Copy filtered JSON data from one file to another using gson library in java

I want to copy JSON fields from one file to another but only after the field satisfies a particular condition, as for example
{"dataset":
[
{"album_id":1,
"album_type":"Live Performance",
"artist_name":"John Doe",....
}
]
}
I want to copy only those records which have a user given artist_name or any other property, else skip the tuple for copying. I am using the following code to add the filtered records to a JSONObject "wr" which I then write to my output file. But its not giving me the desired results
public static void dumpJSONElement(JsonElement element) {
if (element.isJsonObject()) {
JsonObject obj = element.getAsJsonObject();
java.util.Set<java.util.Map.Entry<String,JsonElement>> entries = obj.entrySet();
java.util.Iterator<java.util.Map.Entry<String,JsonElement>> iter = entries.iterator();
while (iter.hasNext()) {
java.util.Map.Entry<String,JsonElement> entry = iter.next();
if(entry.getKey().equals(filterKey)){
if(! entry.getValue().toString().replace("\"", "").equals(filterValue)){
wr.put(entry.getKey(), entry.getValue());
}
}
else{
wr.put(entry.getKey(), entry.getValue());
}
dumpJSONElement(entry.getValue());
}
} else if (element.isJsonArray()) {
JsonArray array = element.getAsJsonArray();
java.util.Iterator<JsonElement> iter = array.iterator();
while (iter.hasNext()) {
JsonElement entry = iter.next();
dumpJSONElement(entry);
}
} else if (element.isJsonPrimitive()) {
JsonPrimitive value = element.getAsJsonPrimitive();
} else if (element.isJsonNull()) {
} else {
System.out.println("Error. Unknown type of element");
}
}
use code below code to convert your json string to generic java type List<Map<Object, Object>>, use code below.
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
public class Test {
public static void main(String... args) {
String str = "[{'id':1,'name':'yogesh'},{'id':2,'name':'aarush', 'degree': 'MCA'}]";
Type type = new TypeToken<List<Map<Object, Object>>>() {
}.getType();
List<Map<Object, Object>> list = new Gson().fromJson(str, type);
System.out.println(new Gson().toJson(list));
filterList(list, "name", "yogesh");
System.out.println(new Gson().toJson(list));
}
public static void filterList(List<Map<Object, Object>> list, String key, Object value) {
for (Map<Object, Object> map : list) {
if (map.containsKey(key)) {
if (map.get(key).equals(value)) {
list.remove(map);
}
}
}
}
}
here i filterd name=yogesh record.
output:
[{"id":1.0,"name":"yogesh"},{"id":2.0,"name":"aarush","degree":"MCA"}]
[{"id":2.0,"name":"aarush","degree":"MCA"}]
I had similar issues and I googled, read a lot about this. In conclusion, the best(most efficient) way (with gson) is to write a custom TypeAdapter for your case.
You can test sample code below (it is working as you expected):
public static void answer() {
String jsonAsText = "{\"dataset\":[{\"album_id\":1,\"album_type\":\"Live Performance\",\"artist_name\":\"John Doe\"},{\"album_id\":2,\"album_type\":\"A Dummy Performance\"}]}";
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(List.class, new AlbumInfoListTypeAdapter());
Gson gson = gsonBuilder.create();
List<AlbumInfo> dataSet = gson.fromJson(jsonAsText, List.class);
System.out.println(gson.toJson(dataSet));
}
private static class AlbumInfo {
int album_id;
String album_type;
String artist_name;
}
private static class AlbumInfoListTypeAdapter extends
TypeAdapter<List<AlbumInfo>> {
#Override
public List<AlbumInfo> read(com.google.gson.stream.JsonReader in)
throws IOException {
List<AlbumInfo> dataSet = new ArrayList<AlbumInfo>();
in.beginObject();
while (in.hasNext()) {
if ("dataset".equals(in.nextName())) {
in.beginArray();
while (in.hasNext()) {
in.beginObject();
AlbumInfo albumInfo = new AlbumInfo();
while (in.hasNext()) {
String jsonTag = in.nextName();
if ("album_id".equals(jsonTag)) {
albumInfo.album_id = in.nextInt();
} else if ("album_type".equals(jsonTag)) {
albumInfo.album_type = in.nextString();
} else if ("artist_name".equals(jsonTag)) {
albumInfo.artist_name = in.nextString();
}
}
in.endObject();
if (albumInfo.artist_name != null && !"".equals(albumInfo.artist_name.trim())) {
dataSet.add(albumInfo);
} else {
System.out.println("Album info ignored because it has no artist_name value");
}
}
in.endArray();
}
}
in.endObject();
return dataSet;
}
#Override
public void write(com.google.gson.stream.JsonWriter out,
List<AlbumInfo> dataSet) throws IOException {
out.beginObject();
out.name("dataset").beginArray();
for (final AlbumInfo albumInfo : dataSet) {
out.beginObject();
out.name("album_id").value(albumInfo.album_id);
out.name("album_type").value(albumInfo.album_type);
out.name("artist_name").value(albumInfo.artist_name);
out.endObject();
}
out.endArray();
out.endObject();
}
}
You can modify the read and the write methods. Gson has many cool functions. I strongly suggest you to read samples at this link.
Edit:
Incoming json text:
{
"dataset": [
{
"album_id": 1,
"album_type": "Live Performance",
"artist_name": "John Doe"
},
{
"album_id": 2,
"album_type": "A Dummy Performance"
}
]
}
The output at System.out.println at answer method:
[
{
"artist_name": "John Doe",
"album_type": "Live Performance",
"album_id": 1
}
]

How to parse a JSON Input stream

I am using java to call a url that returns a JSON object:
url = new URL("my URl");
urlInputStream = url.openConnection().getInputStream();
How can I convert the response into string form and parse it?
I would suggest you have to use a Reader to convert your InputStream in.
BufferedReader streamReader = new BufferedReader(new InputStreamReader(in, "UTF-8"));
StringBuilder responseStrBuilder = new StringBuilder();
String inputStr;
while ((inputStr = streamReader.readLine()) != null)
responseStrBuilder.append(inputStr);
new JSONObject(responseStrBuilder.toString());
I tried in.toString() but it returns:
getClass().getName() + '#' + Integer.toHexString(hashCode())
(like documentation says it derives to toString from Object)
All the current answers assume that it is okay to pull the entire JSON into memory where the advantage of an InputStream is that you can read the input little by little. If you would like to avoid reading the entire Json file at once then I would suggest using the Jackson library (which is my personal favorite but I'm sure others like Gson have similar functions).
With Jackson you can use a JsonParser to read one section at a time. Below is an example of code I wrote that wraps the reading of an Array of JsonObjects in an Iterator. If you just want to see an example of Jackson, look at the initJsonParser, initFirstElement, and initNextObject methods.
public class JsonObjectIterator implements Iterator<Map<String, Object>>, Closeable {
private static final Logger LOG = LoggerFactory.getLogger(JsonObjectIterator.class);
private final InputStream inputStream;
private JsonParser jsonParser;
private boolean isInitialized;
private Map<String, Object> nextObject;
public JsonObjectIterator(final InputStream inputStream) {
this.inputStream = inputStream;
this.isInitialized = false;
this.nextObject = null;
}
private void init() {
this.initJsonParser();
this.initFirstElement();
this.isInitialized = true;
}
private void initJsonParser() {
final ObjectMapper objectMapper = new ObjectMapper();
final JsonFactory jsonFactory = objectMapper.getFactory();
try {
this.jsonParser = jsonFactory.createParser(inputStream);
} catch (final IOException e) {
LOG.error("There was a problem setting up the JsonParser: " + e.getMessage(), e);
throw new RuntimeException("There was a problem setting up the JsonParser: " + e.getMessage(), e);
}
}
private void initFirstElement() {
try {
// Check that the first element is the start of an array
final JsonToken arrayStartToken = this.jsonParser.nextToken();
if (arrayStartToken != JsonToken.START_ARRAY) {
throw new IllegalStateException("The first element of the Json structure was expected to be a start array token, but it was: " + arrayStartToken);
}
// Initialize the first object
this.initNextObject();
} catch (final Exception e) {
LOG.error("There was a problem initializing the first element of the Json Structure: " + e.getMessage(), e);
throw new RuntimeException("There was a problem initializing the first element of the Json Structure: " + e.getMessage(), e);
}
}
private void initNextObject() {
try {
final JsonToken nextToken = this.jsonParser.nextToken();
// Check for the end of the array which will mean we're done
if (nextToken == JsonToken.END_ARRAY) {
this.nextObject = null;
return;
}
// Make sure the next token is the start of an object
if (nextToken != JsonToken.START_OBJECT) {
throw new IllegalStateException("The next token of Json structure was expected to be a start object token, but it was: " + nextToken);
}
// Get the next product and make sure it's not null
this.nextObject = this.jsonParser.readValueAs(new TypeReference<Map<String, Object>>() { });
if (this.nextObject == null) {
throw new IllegalStateException("The next parsed object of the Json structure was null");
}
} catch (final Exception e) {
LOG.error("There was a problem initializing the next Object: " + e.getMessage(), e);
throw new RuntimeException("There was a problem initializing the next Object: " + e.getMessage(), e);
}
}
#Override
public boolean hasNext() {
if (!this.isInitialized) {
this.init();
}
return this.nextObject != null;
}
#Override
public Map<String, Object> next() {
// This method will return the current object and initialize the next object so hasNext will always have knowledge of the current state
// Makes sure we're initialized first
if (!this.isInitialized) {
this.init();
}
// Store the current next object for return
final Map<String, Object> currentNextObject = this.nextObject;
// Initialize the next object
this.initNextObject();
return currentNextObject;
}
#Override
public void close() throws IOException {
IOUtils.closeQuietly(this.jsonParser);
IOUtils.closeQuietly(this.inputStream);
}
}
If you don't care about memory usage, then it would certainly be easier to read the entire file and parse it as one big Json as mentioned in other answers.
For those that pointed out the fact that you can't use the toString method of InputStream like this see https://stackoverflow.com/a/5445161/1304830 :
My correct answer would be then :
import org.json.JSONObject;
public static String convertStreamToString(java.io.InputStream is) {
java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
return s.hasNext() ? s.next() : "";
}
...
JSONObject json = new JSONObject(convertStreamToString(url.openStream());
If you like to use Jackson Databind (which Spring uses by default for its HttpMessageConverters), then you may use the ObjectMapper.readTree(InputStream) API. For example,
ObjectMapper mapper = new ObjectMapper();
JsonNode json = mapper.readTree(myInputStream);
use jackson to convert json input stream to the map or object http://jackson.codehaus.org/
there are also some other usefull libraries for json, you can google: json java
Use a library.
GSON
Jackson
or one of many other JSON libraries that are out there.
Kotlin version with Gson
to read the response JSON:
val response = BufferedReader(
InputStreamReader(conn.inputStream, "UTF-8")
).use { it.readText() }
to parse response we can use Gson:
val model = Gson().fromJson(response, YourModelClass::class.java)
This example reads all objects from a stream of objects,
it is assumed that you need CustomObjects instead of a Map:
ObjectMapper mapper = new ObjectMapper();
JsonParser parser = mapper.getFactory().createParser( source );
if(parser.nextToken() != JsonToken.START_ARRAY) {
throw new IllegalStateException("Expected an array");
}
while(parser.nextToken() == JsonToken.START_OBJECT) {
// read everything from this START_OBJECT to the matching END_OBJECT
// and return it as a tree model ObjectNode
ObjectNode node = mapper.readTree(parser);
CustomObject custom = mapper.convertValue( node, CustomObject.class );
// do whatever you need to do with this object
System.out.println( "" + custom );
}
parser.close();
This answer was composed by using : Use Jackson To Stream Parse an Array of Json Objects and Convert JsonNode into Object
I suggest use javax.json.Json factory as less verbose possible solution:
JsonObject json = Json.createReader(yourInputStream).readObject();
Enjoy!
if you have JSON file you can set it on assets folder then call it using this code
InputStream in = mResources.getAssets().open("fragrances.json");
// where mResources object from Resources class
{
InputStream is = HTTPClient.get(url);
InputStreamReader reader = new InputStreamReader(is);
JSONTokener tokenizer = new JSONTokener(reader);
JSONObject jsonObject = new JSONObject(tokenizer);
}

how to convert object to string in java

I have a function that returns map value (String) as a generic Object. How do I convert it back to string. I tried toString() but all i get is end[Ljava.lang.String;#ff2413
public Object getParameterValue(String key)
{
Iterator iterator=params.entrySet().iterator();
while(iterator.hasNext())
{
Map.Entry me=(Map.Entry)iterator.next();
String[] arr=(String[])me.getValue();
log.info(me.getKey().toString()+"="+arr[0]);
}
if(params.containsKey(key))
{
log.info(key+"="+params.get(key));
return params.get(key);
}
return null;
}
Receiving end
String temp=data.getParameterValue("request").toString();
log.info("end"+temp);
log.info(me.getKey().toString()+"="+arr[0]); give me an output
email=x#as.com
request=login
projectid=as
I'm afraid your map contains something other than String objects. If you call toString() on a String object, you obtain the string itself.
What you get [Ljava.lang.String indicates you might have a String array.
Might not be so related to the issue above. However if you are looking for a way to serialize Java object as string, this could come in hand
package pt.iol.security;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import org.apache.commons.codec.binary.Base64;
public class ObjectUtil {
static final Base64 base64 = new Base64();
public static String serializeObjectToString(Object object) throws IOException {
try (
ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream();
GZIPOutputStream gzipOutputStream = new GZIPOutputStream(arrayOutputStream);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(gzipOutputStream);) {
objectOutputStream.writeObject(object);
objectOutputStream.flush();
return new String(base64.encode(arrayOutputStream.toByteArray()));
}
}
public static Object deserializeObjectFromString(String objectString) throws IOException, ClassNotFoundException {
try (
ByteArrayInputStream arrayInputStream = new ByteArrayInputStream(base64.decode(objectString));
GZIPInputStream gzipInputStream = new GZIPInputStream(arrayInputStream);
ObjectInputStream objectInputStream = new ObjectInputStream(gzipInputStream)) {
return objectInputStream.readObject();
}
}
}
maybe you benefit from converting it to JSON string
String jsonString = new com.google.gson.Gson().toJson(myObject);
in my case, I wanted to add an object to the response headers but you cant add objects to the headers,
so to solve this I convert my object to JSON string and in the client side I will return that string to JSON again
Looking at the output, it seems that your "temp" is a String array. You need to loop across the array to display each value.
The result is not a String but a String[]. That's why you get this unsuspected printout.
[Ljava.lang.String is a signature of an array of String:
System.out.println(new String[]{});
The question how do I convert an object to a String, despite the several answers you see here, and despite the existence of the Object.toString method, is unanswerable, or has infinitely many answers. Because what is being asked for is some kind of text representation or description of the object, and there are infinitely many possible representations. Each representation encodes a particular object instance using a special purpose language (probably a very limited language) or format that is expressive enough to encode all possible object instances.
Before code can be written to convert an object to a String, you must decide on the language/format to be used.
To convert serialize object to String and String to Object
stringToBean(beanToString(new LoginMdp()), LoginMdp.class);
public static String beanToString(Object object) throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
StringWriter stringEmp = new StringWriter();
objectMapper.configure(SerializationFeature.INDENT_OUTPUT, true);
objectMapper.writeValue(stringEmp, object);
return stringEmp.toString();
}
public static <T> T stringToBean(String content, Class<T> valueType) throws IOException {
return new ObjectMapper().readValue(content, valueType);
}
Solution 1: cast
String temp=(String)data.getParameterValue("request");
Solution 2: use typed map:
Map<String, String> param;
So you change Change the return type of your function
public String getParameterValue(String key)
{
if(params.containsKey(key))
{
return params.get(key);
}
return null;
}
and then no need for cast or toString
String temp=data.getParameterValue("request");
toString() is a debug info string. The default implementation returns the class name and the system identity hash. Collections return all elements but arrays not.
Also be aware of NullPointerException creating the log!
In this case a Arrays.toString() may help:
Object temp = data.getParameterValue("request");
String log = temp == null ? "null" : (temp.getClass().isArray() ? Arrays.toString((Object[])temp) : temp.toString());
log.info("end " + temp);
You can also use Arrays.asList():
Object temp = data.getParameterValue("request");
Object log = temp == null ? null : (temp.getClass().isArray() ? Arrays.asList((Object[])temp) : temp);
log.info("end " + temp);
This may result in a ClassCastException for primitive arrays (int[], ...).
/** * This toString-Method works for every Class, where you want to display all the fields and its values */ public String toString() {
StringBuffer sb = new StringBuffer();
Field[] fields = getClass().getDeclaredFields(); //Get all fields incl. private ones
for (Field field : fields){
try {
field.setAccessible(true);
String key=field.getName();
String value;
try{
value = (String) field.get(this);
} catch (ClassCastException e){
value="";
}
sb.append(key).append(": ").append(value).append("\n");
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return sb.toString(); }
You can create toString() method to convert object to string.
int bid;
String bname;
double bprice;
Book(String str)
{
String[] s1 = str.split("-");
bid = Integer.parseInt(s1[0]);
bname = s1[1];
bprice = Double.parseDouble(s1[2]);
}
public String toString()
{
return bid+"-"+bname+"-"+bprice;
}
public static void main(String[] s)
{
Book b1 = new Book("12-JAVA-200.50");
System.out.println(b1);
}

Categories