update java chaincode for JSON serialization determinism

Signed-off-by: fraVlaca <ocsenarf@outlook.com>
This commit is contained in:
Francesco Vlacancich 2021-07-23 10:32:00 +01:00 committed by fraVlaca
parent 235df3271d
commit fc250d3bd5
2 changed files with 61 additions and 155 deletions

View file

@ -10,6 +10,8 @@ import java.util.TreeMap;
import java.util.Map; import java.util.Map;
import java.util.HashMap; import java.util.HashMap;
import com.fasterxml.jackson.core.JsonProcessingException;
import java.io.IOException;
import org.hyperledger.fabric.contract.Context; import org.hyperledger.fabric.contract.Context;
import org.hyperledger.fabric.contract.ContractInterface; import org.hyperledger.fabric.contract.ContractInterface;
@ -22,11 +24,12 @@ import org.hyperledger.fabric.contract.annotation.Transaction;
import org.hyperledger.fabric.shim.ChaincodeException; import org.hyperledger.fabric.shim.ChaincodeException;
import org.hyperledger.fabric.shim.ChaincodeStub; import org.hyperledger.fabric.shim.ChaincodeStub;
import org.hyperledger.fabric.shim.ledger.KeyValue; import org.hyperledger.fabric.shim.ledger.KeyValue;
import org.hyperledger.fabric.shim.ledger.QueryResultsIterator; import org.hyperledger.fabric.shim.ledger.QueryResultsIterator;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson; import com.owlike.genson.Genson;
@Contract( @Contract(
name = "basic", name = "basic",
@ -44,8 +47,8 @@ import com.google.gson.Gson;
@Default @Default
public final class AssetTransfer implements ContractInterface { public final class AssetTransfer implements ContractInterface {
private Gson gson = new Gson(); private final Genson genson = new Genson();
private ObjectMapper om = new ObjectMapper(); private ObjectMapper om = new ObjectMapper().configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);;
private enum AssetTransferErrors { private enum AssetTransferErrors {
ASSET_NOT_FOUND, ASSET_NOT_FOUND,
@ -58,7 +61,7 @@ public final class AssetTransfer implements ContractInterface {
* @param ctx the transaction context * @param ctx the transaction context
*/ */
@Transaction(intent = Transaction.TYPE.SUBMIT) @Transaction(intent = Transaction.TYPE.SUBMIT)
public void InitLedger(final Context ctx) { public void InitLedger(final Context ctx) throws JsonProcessingException{
ChaincodeStub stub = ctx.getStub(); ChaincodeStub stub = ctx.getStub();
CreateAsset(ctx, "asset1", "blue", 5, "Tomoko", 300); CreateAsset(ctx, "asset1", "blue", 5, "Tomoko", 300);
@ -83,7 +86,7 @@ public final class AssetTransfer implements ContractInterface {
*/ */
@Transaction(intent = Transaction.TYPE.SUBMIT) @Transaction(intent = Transaction.TYPE.SUBMIT)
public Asset CreateAsset(final Context ctx, final String assetID, final String color, final int size, public Asset CreateAsset(final Context ctx, final String assetID, final String color, final int size,
final String owner, final int appraisedValue) { final String owner, final int appraisedValue) throws JsonProcessingException{
ChaincodeStub stub = ctx.getStub(); ChaincodeStub stub = ctx.getStub();
if (AssetExists(ctx, assetID)) { if (AssetExists(ctx, assetID)) {
@ -93,28 +96,10 @@ public final class AssetTransfer implements ContractInterface {
} }
Asset asset = new Asset(assetID, color, size, owner, appraisedValue); Asset asset = new Asset(assetID, color, size, owner, appraisedValue);
//deserializing your json into a sorted map, and then serialize the map to get the sorted-by-key json string.
//TreeMap will enforce a sorting order for you, independent of what order they're supplied in
String json = gson.toJson(asset);
try {
om.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);
Map<String, Object> map = om.readValue(json, HashMap.class);
String sortedJson = om.writeValueAsString(map);
stub.putStringState(assetID, sortedJson);
} catch (Exception e) {
}
/*String json = gson.toJson(asset);
TreeMap<String, Object> map = gson.fromJson(json, TreeMap.class);
String sortedJson = gson.toJson(map);
stub.putStringState(assetID, sortedJson);*/
//could use JSON Object approach instead //Use a Jackson ObjectMapper to conver the Asset into string, sort it alphabetically and serialize it into a json string
/*Map<String, String> sortedMap = gson.fromJson(json, TreeMap.class); //this will auto-sort by key alphabetically String sortedJson = om.writeValueAsString(asset);
JSONObject sortedJSON = new JSONObject(); //recreate new JSON object for sorted result stub.putStringState(assetID, sortedJson);
for (Map.Entry<String, String> entry : sortedMap.entrySet()) {
sortedJSON.put(entry.getKey(), entry.getValue());
}
stub.putState(assetID, sortedJson); //wuold do it if possible to write JSONObject to stste */
return asset; return asset;
} }
@ -127,7 +112,7 @@ public final class AssetTransfer implements ContractInterface {
* @return the asset found on the ledger if there was one * @return the asset found on the ledger if there was one
*/ */
@Transaction(intent = Transaction.TYPE.EVALUATE) @Transaction(intent = Transaction.TYPE.EVALUATE)
public Asset ReadAsset(final Context ctx, final String assetID) { public Asset ReadAsset(final Context ctx, final String assetID) throws JsonProcessingException,IOException{
ChaincodeStub stub = ctx.getStub(); ChaincodeStub stub = ctx.getStub();
String assetJSON = stub.getStringState(assetID); String assetJSON = stub.getStringState(assetID);
@ -136,8 +121,8 @@ public final class AssetTransfer implements ContractInterface {
System.out.println(errorMessage); System.out.println(errorMessage);
throw new ChaincodeException(errorMessage, AssetTransferErrors.ASSET_NOT_FOUND.toString()); throw new ChaincodeException(errorMessage, AssetTransferErrors.ASSET_NOT_FOUND.toString());
} }
Asset asset = gson.fromJson(assetJSON, Asset.class); Asset asset = genson.deserialize(assetJSON, Asset.class);
return asset; return asset;
} }
@ -154,7 +139,7 @@ public final class AssetTransfer implements ContractInterface {
*/ */
@Transaction(intent = Transaction.TYPE.SUBMIT) @Transaction(intent = Transaction.TYPE.SUBMIT)
public Asset UpdateAsset(final Context ctx, final String assetID, final String color, final int size, public Asset UpdateAsset(final Context ctx, final String assetID, final String color, final int size,
final String owner, final int appraisedValue) { final String owner, final int appraisedValue) throws JsonProcessingException{
ChaincodeStub stub = ctx.getStub(); ChaincodeStub stub = ctx.getStub();
if (!AssetExists(ctx, assetID)) { if (!AssetExists(ctx, assetID)) {
@ -164,26 +149,10 @@ public final class AssetTransfer implements ContractInterface {
} }
Asset newAsset = new Asset(assetID, color, size, owner, appraisedValue); Asset newAsset = new Asset(assetID, color, size, owner, appraisedValue);
String json = gson.toJson(newAsset);
try {
om.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);
Map<String, Object> map = om.readValue(json, HashMap.class);
String sortedJson = om.writeValueAsString(map);//could also use gson instead
stub.putStringState(assetID, sortedJson);
} catch (Exception e) {
}
/*String json = gson.toJson(asset);
TreeMap<String, Object> map = gson.fromJson(json, TreeMap.class);
String sortedJson = gson.toJson(map);
stub.putStringState(assetID, sortedJson);*/
//could use JSON Object approach instead //Use a Jackson ObjectMapper to conver the Asset into string, sort it alphabetically and serialize it into a json string
/*Map<String, String> sortedMap = gson.fromJson(json, TreeMap.class); //this will auto-sort by key alphabetically String sortedJson = om.writeValueAsString(newAsset);
JSONObject sortedJSON = new JSONObject(); //recreate new JSON object for sorted result stub.putStringState(assetID, sortedJson);
for (Map.Entry<String, String> entry : sortedMap.entrySet()) {
sortedJSON.put(entry.getKey(), entry.getValue());
}
stub.putState(assetID, sortedJson); //wuold do it if possible to write JSONObject to stste */
return newAsset; return newAsset;
} }
@ -231,7 +200,7 @@ public final class AssetTransfer implements ContractInterface {
* @return the updated asset * @return the updated asset
*/ */
@Transaction(intent = Transaction.TYPE.SUBMIT) @Transaction(intent = Transaction.TYPE.SUBMIT)
public Asset TransferAsset(final Context ctx, final String assetID, final String newOwner) { public Asset TransferAsset(final Context ctx, final String assetID, final String newOwner) throws JsonProcessingException{
ChaincodeStub stub = ctx.getStub(); ChaincodeStub stub = ctx.getStub();
String assetJSON = stub.getStringState(assetID); String assetJSON = stub.getStringState(assetID);
@ -241,29 +210,13 @@ public final class AssetTransfer implements ContractInterface {
throw new ChaincodeException(errorMessage, AssetTransferErrors.ASSET_NOT_FOUND.toString()); throw new ChaincodeException(errorMessage, AssetTransferErrors.ASSET_NOT_FOUND.toString());
} }
Asset asset = gson.fromJson(assetJSON, Asset.class); //Asset asset = gson.fromJson(assetJSON, Asset.class);
Asset asset = genson.deserialize(assetJSON, Asset.class);
Asset newAsset = new Asset(asset.getAssetID(), asset.getColor(), asset.getSize(), newOwner, asset.getAppraisedValue()); Asset newAsset = new Asset(asset.getAssetID(), asset.getColor(), asset.getSize(), newOwner, asset.getAppraisedValue());
String json = gson.toJson(newAsset);
try {
om.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);
Map<String, Object> map = om.readValue(json, HashMap.class);
String sortedJson = om.writeValueAsString(map);
stub.putStringState(assetID, sortedJson);
} catch (Exception e) {
}
/*String json = gson.toJson(asset);
TreeMap<String, Object> map = gson.fromJson(json, TreeMap.class);
String sortedJson = gson.toJson(map);
stub.putStringState(assetID, sortedJson);*/
//could use JSON Object approach instead //Use a Jackson ObjectMapper to conver the Asset into string, sort it alphabetically and serialize it into a json string
/*Map<String, String> sortedMap = gson.fromJson(json, TreeMap.class); //this will auto-sort by key alphabetically String sortedJson = om.writeValueAsString(newAsset);
JSONObject sortedJSON = new JSONObject(); //recreate new JSON object for sorted result stub.putStringState(assetID, sortedJson);
for (Map.Entry<String, String> entry : sortedMap.entrySet()) {
sortedJSON.put(entry.getKey(), entry.getValue());
}
stub.putState(assetID, sortedJson); //wuold do it if possible to write JSONObject to stste */
return newAsset; return newAsset;
} }
@ -275,7 +228,7 @@ public final class AssetTransfer implements ContractInterface {
* @return array of assets found on the ledger * @return array of assets found on the ledger
*/ */
@Transaction(intent = Transaction.TYPE.EVALUATE) @Transaction(intent = Transaction.TYPE.EVALUATE)
public String GetAllAssets(final Context ctx) { public String GetAllAssets(final Context ctx) throws JsonProcessingException,IOException{
ChaincodeStub stub = ctx.getStub(); ChaincodeStub stub = ctx.getStub();
List<Asset> queryResults = new ArrayList<Asset>(); List<Asset> queryResults = new ArrayList<Asset>();
@ -287,13 +240,13 @@ public final class AssetTransfer implements ContractInterface {
QueryResultsIterator<KeyValue> results = stub.getStateByRange("", ""); QueryResultsIterator<KeyValue> results = stub.getStateByRange("", "");
for (KeyValue result: results) { for (KeyValue result: results) {
Asset asset = gson.fromJson(result.getStringValue(), Asset.class); Asset asset = genson.deserialize(result.getStringValue(), Asset.class);
System.out.println(asset);
queryResults.add(asset); queryResults.add(asset);
System.out.println(asset.toString());
} }
final String response = gson.toJson(queryResults); //Set pretty printing of json
final String response = genson.serialize(queryResults);
return response; return response;
} }
} }

View file

@ -10,6 +10,8 @@ import java.util.TreeMap;
import java.util.Map; import java.util.Map;
import java.util.HashMap; import java.util.HashMap;
import com.fasterxml.jackson.core.JsonProcessingException;
import java.io.IOException;
import org.hyperledger.fabric.contract.Context; import org.hyperledger.fabric.contract.Context;
import org.hyperledger.fabric.contract.ContractInterface; import org.hyperledger.fabric.contract.ContractInterface;
@ -22,11 +24,12 @@ import org.hyperledger.fabric.contract.annotation.Transaction;
import org.hyperledger.fabric.shim.ChaincodeException; import org.hyperledger.fabric.shim.ChaincodeException;
import org.hyperledger.fabric.shim.ChaincodeStub; import org.hyperledger.fabric.shim.ChaincodeStub;
import org.hyperledger.fabric.shim.ledger.KeyValue; import org.hyperledger.fabric.shim.ledger.KeyValue;
import org.hyperledger.fabric.shim.ledger.QueryResultsIterator; import org.hyperledger.fabric.shim.ledger.QueryResultsIterator;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson; import com.owlike.genson.Genson;
@Contract( @Contract(
name = "basic", name = "basic",
@ -44,8 +47,8 @@ import com.google.gson.Gson;
@Default @Default
public final class AssetTransfer implements ContractInterface { public final class AssetTransfer implements ContractInterface {
private Gson gson = new Gson(); private final Genson genson = new Genson();
private ObjectMapper om = new ObjectMapper(); private ObjectMapper om = new ObjectMapper().configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);;
private enum AssetTransferErrors { private enum AssetTransferErrors {
ASSET_NOT_FOUND, ASSET_NOT_FOUND,
@ -58,7 +61,7 @@ public final class AssetTransfer implements ContractInterface {
* @param ctx the transaction context * @param ctx the transaction context
*/ */
@Transaction(intent = Transaction.TYPE.SUBMIT) @Transaction(intent = Transaction.TYPE.SUBMIT)
public void InitLedger(final Context ctx) { public void InitLedger(final Context ctx) throws JsonProcessingException{
ChaincodeStub stub = ctx.getStub(); ChaincodeStub stub = ctx.getStub();
CreateAsset(ctx, "asset1", "blue", 5, "Tomoko", 300); CreateAsset(ctx, "asset1", "blue", 5, "Tomoko", 300);
@ -83,7 +86,7 @@ public final class AssetTransfer implements ContractInterface {
*/ */
@Transaction(intent = Transaction.TYPE.SUBMIT) @Transaction(intent = Transaction.TYPE.SUBMIT)
public Asset CreateAsset(final Context ctx, final String assetID, final String color, final int size, public Asset CreateAsset(final Context ctx, final String assetID, final String color, final int size,
final String owner, final int appraisedValue) { final String owner, final int appraisedValue) throws JsonProcessingException{
ChaincodeStub stub = ctx.getStub(); ChaincodeStub stub = ctx.getStub();
if (AssetExists(ctx, assetID)) { if (AssetExists(ctx, assetID)) {
@ -93,28 +96,10 @@ public final class AssetTransfer implements ContractInterface {
} }
Asset asset = new Asset(assetID, color, size, owner, appraisedValue); Asset asset = new Asset(assetID, color, size, owner, appraisedValue);
//deserializing your json into a sorted map, and then serialize the map to get the sorted-by-key json string.
//TreeMap will enforce a sorting order for you, independent of what order they're supplied in
String json = gson.toJson(asset);
try {
om.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);
Map<String, Object> map = om.readValue(json, HashMap.class);
String sortedJson = om.writeValueAsString(map);
stub.putStringState(assetID, sortedJson);
} catch (Exception e) {
}
/*String json = gson.toJson(asset);
TreeMap<String, Object> map = gson.fromJson(json, TreeMap.class);
String sortedJson = gson.toJson(map);
stub.putStringState(assetID, sortedJson);*/
//could use JSON Object approach instead //Use a Jackson ObjectMapper to conver the Asset into string, sort it alphabetically and serialize it into a json string
/*Map<String, String> sortedMap = gson.fromJson(json, TreeMap.class); //this will auto-sort by key alphabetically String sortedJson = om.writeValueAsString(asset);
JSONObject sortedJSON = new JSONObject(); //recreate new JSON object for sorted result stub.putStringState(assetID, sortedJson);
for (Map.Entry<String, String> entry : sortedMap.entrySet()) {
sortedJSON.put(entry.getKey(), entry.getValue());
}
stub.putState(assetID, sortedJson); //wuold do it if possible to write JSONObject to stste */
return asset; return asset;
} }
@ -127,7 +112,7 @@ public final class AssetTransfer implements ContractInterface {
* @return the asset found on the ledger if there was one * @return the asset found on the ledger if there was one
*/ */
@Transaction(intent = Transaction.TYPE.EVALUATE) @Transaction(intent = Transaction.TYPE.EVALUATE)
public Asset ReadAsset(final Context ctx, final String assetID) { public Asset ReadAsset(final Context ctx, final String assetID) throws JsonProcessingException,IOException{
ChaincodeStub stub = ctx.getStub(); ChaincodeStub stub = ctx.getStub();
String assetJSON = stub.getStringState(assetID); String assetJSON = stub.getStringState(assetID);
@ -136,8 +121,8 @@ public final class AssetTransfer implements ContractInterface {
System.out.println(errorMessage); System.out.println(errorMessage);
throw new ChaincodeException(errorMessage, AssetTransferErrors.ASSET_NOT_FOUND.toString()); throw new ChaincodeException(errorMessage, AssetTransferErrors.ASSET_NOT_FOUND.toString());
} }
Asset asset = gson.fromJson(assetJSON, Asset.class); Asset asset = genson.deserialize(assetJSON, Asset.class);
return asset; return asset;
} }
@ -154,7 +139,7 @@ public final class AssetTransfer implements ContractInterface {
*/ */
@Transaction(intent = Transaction.TYPE.SUBMIT) @Transaction(intent = Transaction.TYPE.SUBMIT)
public Asset UpdateAsset(final Context ctx, final String assetID, final String color, final int size, public Asset UpdateAsset(final Context ctx, final String assetID, final String color, final int size,
final String owner, final int appraisedValue) { final String owner, final int appraisedValue) throws JsonProcessingException{
ChaincodeStub stub = ctx.getStub(); ChaincodeStub stub = ctx.getStub();
if (!AssetExists(ctx, assetID)) { if (!AssetExists(ctx, assetID)) {
@ -164,26 +149,10 @@ public final class AssetTransfer implements ContractInterface {
} }
Asset newAsset = new Asset(assetID, color, size, owner, appraisedValue); Asset newAsset = new Asset(assetID, color, size, owner, appraisedValue);
String json = gson.toJson(newAsset);
try {
om.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);
Map<String, Object> map = om.readValue(json, HashMap.class);
String sortedJson = om.writeValueAsString(map);//could also use gson instead
stub.putStringState(assetID, sortedJson);
} catch (Exception e) {
}
/*String json = gson.toJson(asset);
TreeMap<String, Object> map = gson.fromJson(json, TreeMap.class);
String sortedJson = gson.toJson(map);
stub.putStringState(assetID, sortedJson);*/
//could use JSON Object approach instead //Use a Jackson ObjectMapper to conver the Asset into string, sort it alphabetically and serialize it into a json string
/*Map<String, String> sortedMap = gson.fromJson(json, TreeMap.class); //this will auto-sort by key alphabetically String sortedJson = om.writeValueAsString(newAsset);
JSONObject sortedJSON = new JSONObject(); //recreate new JSON object for sorted result stub.putStringState(assetID, sortedJson);
for (Map.Entry<String, String> entry : sortedMap.entrySet()) {
sortedJSON.put(entry.getKey(), entry.getValue());
}
stub.putState(assetID, sortedJson); //wuold do it if possible to write JSONObject to stste */
return newAsset; return newAsset;
} }
@ -231,7 +200,7 @@ public final class AssetTransfer implements ContractInterface {
* @return the updated asset * @return the updated asset
*/ */
@Transaction(intent = Transaction.TYPE.SUBMIT) @Transaction(intent = Transaction.TYPE.SUBMIT)
public Asset TransferAsset(final Context ctx, final String assetID, final String newOwner) { public Asset TransferAsset(final Context ctx, final String assetID, final String newOwner) throws JsonProcessingException{
ChaincodeStub stub = ctx.getStub(); ChaincodeStub stub = ctx.getStub();
String assetJSON = stub.getStringState(assetID); String assetJSON = stub.getStringState(assetID);
@ -241,29 +210,13 @@ public final class AssetTransfer implements ContractInterface {
throw new ChaincodeException(errorMessage, AssetTransferErrors.ASSET_NOT_FOUND.toString()); throw new ChaincodeException(errorMessage, AssetTransferErrors.ASSET_NOT_FOUND.toString());
} }
Asset asset = gson.fromJson(assetJSON, Asset.class); //Asset asset = gson.fromJson(assetJSON, Asset.class);
Asset asset = genson.deserialize(assetJSON, Asset.class);
Asset newAsset = new Asset(asset.getAssetID(), asset.getColor(), asset.getSize(), newOwner, asset.getAppraisedValue()); Asset newAsset = new Asset(asset.getAssetID(), asset.getColor(), asset.getSize(), newOwner, asset.getAppraisedValue());
String json = gson.toJson(newAsset);
try {
om.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);
Map<String, Object> map = om.readValue(json, HashMap.class);
String sortedJson = om.writeValueAsString(map);
stub.putStringState(assetID, sortedJson);
} catch (Exception e) {
}
/*String json = gson.toJson(asset);
TreeMap<String, Object> map = gson.fromJson(json, TreeMap.class);
String sortedJson = gson.toJson(map);
stub.putStringState(assetID, sortedJson);*/
//could use JSON Object approach instead //Use a Jackson ObjectMapper to conver the Asset into string, sort it alphabetically and serialize it into a json string
/*Map<String, String> sortedMap = gson.fromJson(json, TreeMap.class); //this will auto-sort by key alphabetically String sortedJson = om.writeValueAsString(newAsset);
JSONObject sortedJSON = new JSONObject(); //recreate new JSON object for sorted result stub.putStringState(assetID, sortedJson);
for (Map.Entry<String, String> entry : sortedMap.entrySet()) {
sortedJSON.put(entry.getKey(), entry.getValue());
}
stub.putState(assetID, sortedJson); //wuold do it if possible to write JSONObject to stste */
return newAsset; return newAsset;
} }
@ -275,7 +228,7 @@ public final class AssetTransfer implements ContractInterface {
* @return array of assets found on the ledger * @return array of assets found on the ledger
*/ */
@Transaction(intent = Transaction.TYPE.EVALUATE) @Transaction(intent = Transaction.TYPE.EVALUATE)
public String GetAllAssets(final Context ctx) { public String GetAllAssets(final Context ctx) throws JsonProcessingException,IOException{
ChaincodeStub stub = ctx.getStub(); ChaincodeStub stub = ctx.getStub();
List<Asset> queryResults = new ArrayList<Asset>(); List<Asset> queryResults = new ArrayList<Asset>();
@ -287,13 +240,13 @@ public final class AssetTransfer implements ContractInterface {
QueryResultsIterator<KeyValue> results = stub.getStateByRange("", ""); QueryResultsIterator<KeyValue> results = stub.getStateByRange("", "");
for (KeyValue result: results) { for (KeyValue result: results) {
Asset asset = gson.fromJson(result.getStringValue(), Asset.class); Asset asset = genson.deserialize(result.getStringValue(), Asset.class);
System.out.println(asset);
queryResults.add(asset); queryResults.add(asset);
System.out.println(asset.toString());
} }
final String response = gson.toJson(queryResults); //Set pretty printing of json
final String response = genson.serialize(queryResults);
return response; return response;
} }
} }