ServiceStack: How do I Serialize a nested object of Dictionary<string, object>? - json

"event-data":
{
"event": "opened",
"timestamp": 1529006854.329574,
"id": "DACSsAdVSeGpLid7TN03WA",
"delivery-status": {
"title": "success"
}
}
//Structure
public List<Dictionary<string, object>> EventData { get; set; } = new List<Dictionary<string, object>>();
var json = ServiceStack.Text.JsonSerializer.SerializeToString(request.EventData);
So clearly this Jsonifies the object, but only at the root level. Every child object becomes riddled with \n and \t escapes... so it's just flat stringing the children.
What's the proper (Fastest) way to make this just raw nested Json?

You can use the late-bound generic Dictionary<string, object> and List<object>, e.g:
var obj = new Dictionary<string, object> {
["event-data"] = new Dictionary<string, object> {
["event"] = "opened",
["timestamp"] = 1529006854.329574,
["id"] = "DACSsAdVSeGpLid7TN03WA",
["delivery-status"] = new Dictionary<string,object> {
["title"] = "success"
}
}
};
obj.ToJson().IndentJson().Print();
Prints out:
{
"event-data": {
"event": "opened",
"timestamp": 1529006854.329574,
"id": "DACSsAdVSeGpLid7TN03WA",
"delivery-status": {
"title": "success"
}
}
}
When in doubt, you can use JS Utils to parse any arbitrary JSON which will parse them in late-bound generic collections:
var dto = JSON.parse(json);
dto.ToJson().IndentJson().Print();
Note: JS Utils is better at deserializing unknown JSON whilst ServiceStack.Text JSON Serializer is better about deserializing into typed POCOs.

Related

Storing Document with generic JsonNode in ElasticSearch using Spring Data

I want to store unspecified json payload data in an Elasticsearch index using Spring Data with the following entity
#Document(indexName = "message")
public class Message {
#Id
private String id;
private JsonNode payload;
//getters and setters here
}
The payload varies and needs to be stored in a generic way that can also be easily loaded again that's why I'd like to use the JsonNode here.
A document with "id" gets written but the field "payload" is empty.
When I look up the document written to the index in Kibana it looks like this:
_class:
com.tryout.Message
payload:
id:
30243006-0844-4438-a7f0-db93518b340f
_id:
30243006-0844-4438-a7f0-db93518b340f
_type:
_doc
_index:
message
_score:
0
In the index mapping "payload" also wasn't created and it looks like this:
{
"mappings": {
"_doc": {
"properties": {
"_class": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"id": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
}
}
Any ideas how I can get my generic payload stored?
(I'm using Elastic v7.9.2 & Spring Booot + spring-boot-data-jpa v2.3.5 and spring-data-elasticsearch v4.1.1)
I had this problem after upgrading spring-data-elastic-search version to 4, and unfortunately I didn't find a clear answer for it. Reading spring data elasticsearch documentation,I found out this:
As of version 4.0 only the Meta Object Mapping is used, the Jackson based mapper is not available anymore and the MappingElasticsearchConverter is used.
After searching several days, finally I solve my problem this way, I hope it could help others.
p.s : JsonNode can include jsonArray or JsonObject, so these two data-types should be handled while reading/writing.
class JsonElasticSearchConverter extends MappingElasticsearchConverter {
private CustomConversions conversions = new ElasticsearchCustomConversions(Collections.emptyList());
CustomElasticSearchConverter(MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> mappingContext) {
super(mappingContext);
setConversions(conversions);
}
CustomElasticSearchConverter(MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> mappingContext, GenericConversionService conversionService) {
super(mappingContext, conversionService);
setConversions(conversions);
}
#Override
protected <R> R readValue(#Nullable Object source, ElasticsearchPersistentProperty property,
TypeInformation<R> targetType) {
if (source == null) {
return null;
}
if (targetType.getType() == JsonNode.class) {
return (R) mapStoredValueToJsonNode(source);
}
return super.readValue(source, property, targetType);
}
#Override
protected Object getWriteComplexValue(ElasticsearchPersistentProperty property, TypeInformation<?> typeHint, Object value) {
if (typeHint.getType() == JsonNode.class) {
return getWriteJsonNodeValue(value);
}
return super.getWriteComplexValue(property, typeHint, value);
}
private JsonNode mapStoredValueToJsonNode(Object source) {
JsonNode jsonNode = null;
ObjectMapper mapper = new ObjectMapper();
if (source instanceof ArrayList) {
ArrayNode array = mapper.valueToTree(source);
jsonNode = mapper.valueToTree(array);
}
if (source instanceof HashMap) {
jsonNode = mapper.convertValue(source, JsonNode.class);
}
return jsonNode;
}
private Object getWriteJsonNodeValue(Object value) {
JsonNode jsonNode = null;
ObjectMapper mapper = new ObjectMapper();
if (value instanceof ObjectNode)
try {
jsonNode = mapper.readTree(value.toString());
} catch (IOException shouldNotHappened) {
//log.warn("This error should not happened" + shouldNotHappened.getMessage());
}
else if (value instanceof ArrayNode) {
ArrayNode array = mapper.valueToTree(value);
jsonNode = mapper.valueToTree(array);
}
return jsonNode;
}
}

get field list from json object list with Jackson

I have JSON data that is getting from api, I want to get list of a field inside of json data.
JSON data :
[
{
"code":"en",
"native":true,
"plurals":{
"length":2,
"forms":[
"one",
"other"
]
}
}, {
"code":"de",
"native":true,
"plurals":{
"length":2,
"forms":[
"one",
"other"
]
}
}, {
"code":"le",
"native":true,
"plurals":{
"length":2,
"forms":[
"one",
"other"
]
}
}
]
I want to get code fields data as list<String> like below :
["en","de","le"]
what is the easiest way to do this?
Note : I am using Spring's RestTemplate to get data.
Use the findValues method to extract the value of all properties named "code":
ObjectMapper om = new ObjectMapper();
JsonNode tree = om.readTree(json);
List<JsonNode> code = tree.findValues("code");
Running it on your example data gives the result
["en", "de", "le"]
Try below method.
public static void main(String[] args) throws JSONException {
String jsonString = jsonResponseFromApi;
JSONObject obj= new JSONObject();
JSONObject jsonObject = obj.fromObject(jsonString);
ArrayList<String> list = new ArrayList<String>();
for(int i=0; i<jsonObject.length(); i++){
list.add(jsonObject.getJSONObject(i).getString("code"));
}
System.out.println(list);
}
}
Refer below thread for more details How to Parse this JSON Response in JAVA

Deserializing JSON objects with variable structure, GSON

I'm trying to deserialize JSON with the following structure using GSON:
"FKeyColumn": [{
"ForeignKeyColumns": {
"Id": "Id"
},
"ReferenceTable": "Expense_Group_Configurations"
}],
"FKeyColumn": [{
"ForeignKeyColumns": {
"Vid": "Id"
},
"ReferenceTable": "Expense_Group_Configurations"
}]
...
I'm not sure how to structure my ForeignKeyColumns class though, since the JSON object it represents contains arbitrary key-value pairings (there's no set structure aside from the object always being named ForeignKeyColumns). How can I parse this with GSON?
It required a custom deserializer for FKeyColumn objects:
public class FKeyColumnDeserializer implements JsonDeserializer<FKeyColumn> {
public FKeyColumn deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context)
throws JsonParseException {
final JsonArray jsonFKArray = json.getAsJsonArray();
final FKeyColumn fkc = new FKeyColumn();
for (int i = 0; i < jsonFKArray.size(); i++) {
final JsonObject fkObject = (JsonObject) jsonFKArray.get(i);
final String ReferenceTable = fkObject.get("ReferenceTable").getAsString();
final JsonObject ForeignKeyColumns = (JsonObject) fkObject.get("ForeignKeyColumns");
Set<Entry<String, JsonElement>> entrySet = ForeignKeyColumns.entrySet();
for(HashMap.Entry<String, JsonElement> entry : entrySet){
fkc.fkRefTableMap.put(entry.getKey(), ReferenceTable);
}
}
return fkc;
}
}

depending on input json array return output json in literal json in spring mvc

My controller
#Controller
public class HomeController {
#RequestMapping(value = "/trans", method = RequestMethod.POST)
public #ResponseBody String jsontest(#RequestBody String transjson) throws JsonProcessingException, IOException, JSONException {
try{
JsonParser parser = new JsonParser();
parser.parse(transjson);
String output = null;
JSONObject obj1 = new JSONObject(transjson);
int size=obj1.getJSONArray("inArray").length();
/* this is where I need help.*/
for(int i=0;i<size;i++){
output="{\"outArray\":[{\"in\":\""+obj1.getJSONArray("inArray").get(i)+"\",\"out\":\"hie\"}]}";
}
return output;
}
catch(JsonSyntaxException ex)
{
System.out.println(ex.getMessage());
return "{\"message\":\"Invalid JSON\"}";
}
catch(Exception ex)
{
System.out.println(ex.getMessage());
return "{\"message\":\"Exception occured\"}";
}
}
}
what I want is if my input JSON is
{
"inArray": [
"first"
]
}
then my output should be
{
"outArray": [
{
"in": "first",
"out": "hie"
}
]
}
if my input JSON is
{
"inArray": [
"first",
"second"
]
}
then my output should be
{
"outArray": [
{
"in": "first",
"out": "hie"
},
{
"in": "second",
"out": "hie"
}
]
}
means I don't know the number of the elements in the input array in the input json. And i need make my output in the literal json format on the basis of input elemen numbers as shown above.
I tried few things but have not been able to get the desired output.
I have been able to do this.
Actually I was trying to make string object but instead of that if I make jsonobject and put everything in it and at last return that jsonobject.tostring() form then the desired resultcan be obtained.
int size=obj1.getJSONArray("inArray").length();
JSONArray ja = new JSONArray();
for(int i=0;i<size;i++){
JSONObject jo = new JSONObject();
jo.put("in", obj1.getJSONArray("inArray").get(i));
jo.put("out", "hie");
ja.put(jo);
}
JSONObject mainObj = new JSONObject();
mainObj.put("outArray", ja);
return mainObj.toString();

How can I use the Gson to parse a json that keep the same key but not the same type

Actually,I want to parse the json like this:
{
"contents": [
{
"type": "image",
"data": {
"attachment": "picurl",
"width": 600,
"height": 398
}
},
{
"type": "text",
"data": "something like xxx"
}
]
}
as you can see, the key "data" sometimes is a String, and sometings is json object, what should I do to parse this json by Gson?
The way I do this is deserialize twice. First into an class that defines only type.
class TypeObject {
public String type;
}
After the first deserializiation, you can read the type, and discover which is the real target object you should deserialize into.
De-serializing twice obviously isn't ideal, but that's the unfortunate nature of using a static language to de-serialize a JSON string with non uniform objects in a list.
I have find the way the parse that json, as Alexis C said, I custom a deserializer, like that
#Override
public MessageDiv deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
MessageDiv messageDiv = new MessageDiv();
JsonObject obj = jsonElement.getAsJsonObject();
String msgtype = obj.get("type").getAsString();
messageDiv.setType(msgtype);
if (msgtype.equals("text")) {
messageDiv.setContent(obj.get("data").getAsString());
} else {
JsonObject imgObj = obj.get("data").getAsJsonObject();
DivData dd = new DivData();
dd.setAttachment(imgObj.get("attachment").getAsString());
dd.setHeight(imgObj.get("height").getAsInt());
dd.setWidth(imgObj.get("width").getAsInt());
messageDiv.setData(dd);
}
return messageDiv;
}
About how to custom o deserializer, click here, it's userful for me!