I want to generate JSON output similar to this one:
var json = {
id: "1",
name: "AAA",
children: [{
id: "2",
name: "BBB",
data: {
relation: "<b>Connections:</b><ul><li> AAA <div>(relation: direct)</div></li><li> CCC <div>(relation: direct)</div></li></ul>"
},
children: [{
id: "3",
name: "CCC",
data: {
relation: "<b>Connections:</b><ul><li> BBB <div>(relation: direct)</div></li></ul>"
},
children: []
}]
}, ....
Here is what I did so far:
grails create-domain-resource json.Object
class Object{
String name
String relation
static hasMany = [children: Object]
public String getData() {
def writer = new StringWriter()
_object.gson
//json g.render(hero)
json {
//data hero.data
data: {relation hero.relation}
name hero.name
}
Problem
I'm not able to produce:
data: {
relation: "<b>Connections:</b><ul><li> BBB <div>(relation: direct)</div></li></ul>"
},
Questions:
1) I've read the official documentation but I'm not able to find how to do transient fields
2) What is best approach of mixing json and xml / html.
3) How to pass json code to another view variable
thank you in advance
You can check the official documentation for gson views at:
http://views.grails.org/latest/#_json_view_api
Domain class
class Object{
String name
String relation
static hasMany = [children: Object]
public String getRelation() {
Template 1 object.gson
import json.Object
model {
Object object
}
json tmpl.object(object)
Template 2 _object.gson
import json.Object
model {
Object object
}
json {
id object.id
data(relation: object.relation)
name object.name
children g.render(object.children,[ excludes:['exclude_fields']])
//children g.render(object.children,[resolveTemplate: false]) // one to many relations - avoid circular error
//object2 object.book.name // one to one relations
}
ObjectController
import grails.plugin.json.view.JsonViewTemplateEngine
import org.springframework.beans.factory.annotation.Autowired
#Autowired
JsonViewTemplateEngine templateEngine
def test() {
def t = templateEngine.resolveTemplate('/object/object')
def writable = t.make(object: Object.get(params.id))
def sw = new StringWriter()
writable.writeTo( sw )
return [json:sw]
}
Questions:
1) I've read the official documentation but I'm not able to find how to do transient fields - you can use Named arguments which are valid values for objects or getters
2) What is best approach of mixing json and xml / html. - **I guess you can check : http://docs.groovy-lang.org/latest/html/gapi/groovy/json/StreamingJsonBuilder.html **
3) How to pass json code to another view variable - **check code above at objectController **
Related
I want to POST some custom JSON to my postgres jsonb column via postman using the below request.
The custom part is sent in the "Settings" > "data" node. I don't want to apply the custom part to a model I just want to send in any kind of json and store it.
{
"name": "Test",
"settings": {
"data": {
"customdata": "hello",
"custommore": "bye"
}
}
}
The "data" node is modelled - like this:
public string Data { get; set; } //I have tried JSONDocument and Jsonb types to no avail.
Postman errors with this:
"errors": {
"$.settings.data": [
"The JSON value could not be converted to System.String. Path: $.settings.data | LineNumber: 3 | BytePositionInLine: 17."
]
}
The request doesn't even hit my controller method. I think it is because the customdata and custommore is not mapped to a model.
Is there a way of sending in custom JSON data that is not fixed to a model of any kind - or must it be part of a model?
I'm struggling to find anything about this that doesn't relate to EF core which is not what I am using.
You can use custom model binding,and get json data from HttpContext.Request.Body,and then use sonConvert.DeserializeObject to get json object.You can set the data to the format you want.
Here is a demo:
DataBinder:
public class DataBinder:IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
{
throw new ArgumentNullException(nameof(bindingContext));
}
var model1 = new Customer();
using (var reader = new StreamReader(bindingContext.HttpContext.Request.Body))
{
var body = reader.ReadToEndAsync();
var mydata = JsonConvert.DeserializeObject<JObject>(body.Result);
model1.Name = mydata["name"].ToString();
model1.Settings = new Settings
{
Data = mydata["settings"]["data"].ToString()
};
}
bindingContext.Result = ModelBindingResult.Success(model1);
return Task.CompletedTask;
}
}
Controller:
public IActionResult TestCustomModelBinding([ModelBinder(BinderType = typeof(DataBinder))]Customer customer) {
return Ok();
}
result:
creating hard coded json is easy, e.g.
String createJson(Person person, list<Account> accounts) {
def builder = new JsonBuilder()
def json = builder {
person person
accounts accounts
}
return builder.toPrettyString()
}
The above works, and produces something like this:
{
"person":{
username": "user"
"firstName": "test"
}
"accounts":[
{
"balance": "200829.00",
"currency": "CRD",
"id": 1,
}
]
}
The problem is we have a REST api, which returns JSON. Curently, we have a lot of duplicate code, as we can't find a generic way to generate different parts of the JSON api response and combine them together and render the result, ether by merging json strings, or by dynamically buidling the json from a map, e.g the following doesnt work:
String createJson(Map map) {
def builder = new JsonBuilder()
def root = builder {
map.collect { key, value ->
"$key" value
}
}
return builder.toPrettyString()
}
then calling it like this:
Person person = someMethodToGetAPerson()
List<Account> accounts = someMethodToGetAccounts(person)
Map map = ["person", person, "accounts", accounts]
String json = createJson(map)
render(status: 200, contentType: 'application/json', text: json)
However, this fails, with a stack overflow in the bowels of grails.
In addition, we have defined several json marshallers which must be used, e.g.
JSON.registerObjectMarshaller(Account) {
return [balance: formatter.format(it.balance)....
}
Any ideas?
What I could understand is you want to convert a map into JSON string. For that you can use grails.converters.JSON class. For example
Person person = someMethodToGetAPerson()
List<Account> accounts = someMethodToGetAccounts(person)
Map map = [person: person, accounts: accounts]
String json = new JSON(map).toString()
The toString() method also takes an boolean value for preety printing. And it should honor your registered marshallers
I created a Map of custom types by using information found here http://www.blaenkdenum.com/notes/play/:
def getAllProductsWithStockItems: Map[Product, List[StockItem]] = {
DB.withConnection { implicit connection =>
val sql = SQL("select p.*, s.*" +
"from products p" +
"inner join stock_items s on (p.id = s.product_id)")
val results: List[(Product, StockItem)] = sql.as(productStockItemParser *)
results.groupBy { _._1 }.mapValues { _.map { _._2 } }
}
}
I need to serialize/deserialize Map[Product, List[StockItem]] into JSON, but I don't really know how to do it. I already have working serializers for both custom types (Product and StockItem) which I coded using Play's native JSON library, but I can use another one if it's better. Can someone give me some pointers?
Update: The List/StockItem example is not a good one, but it's just an abstraction. In the end, I have a structure which represents 1 to n relationships.
So, considering each Product has a list of StockItem, I expect the JSON to look like something similar to this:
{
"products": [
{
"id": 1
"productName": "coffee",
"stockitems": [
{
"id":1,
"brandName": "Crazycoffee",
"price": 5
"quantity": 3000
},
{
"id":2,
"brandName": "Badcoffee",
"price": 1
"quantity": 2000
}
]
},
{
"id": 1
"productName": "ice cream",
"stockitems": [
{
"id":1,
"brandName": "Sleazyice",
"price": 2
"quantity": 300
}
]
}
]
}
I haven't tested it, but it is writing from part of my working java code. Using Jackson library (included in play 2) :
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
public static String toJson(Map<Product, List<StockItem>> lst)
{
JsonFactory factory = new JsonFactory();
CharArrayWriter writer = new CharArrayWriter();
try{
JsonGenerator gen = factory.createGenerator(writer);
gen.writeStartObject();
gen.writeFieldName("infos");
gen.writeRaw(":" + listToJson(lst));
gen.writeEndObject();
gen.close();
}catch(Throwable e){
Logger.error("AjaxRequest.generateResponse: " + e.getMessage());
e.printStackTrace();
}
return new String(writer.toCharArray());
}
/**
* Convert a list into a json string.
*/
public static String listToJson(List<?> lst) throws IOException
{
ObjectMapper mapper = new ObjectMapper();
JsonFactory factory = new JsonFactory();
CharArrayWriter writer = new CharArrayWriter();
JsonGenerator gen = factory.createGenerator(writer);
gen.writeRaw(mapper.writeValueAsString(lst));
gen.close();
return new String(writer.toCharArray());
}
Jackson library work well, but is so far from being easy to understand. Anyway, with few modifications, this code should work.
You could consider using https://playframework.com/documentation/2.5.x/ScalaJsonAutomated , then you could save some time while not having to write all the Writers and Readers by hand. You could create a "wrapper" entity based on your top level JSON element "products" such as ProductsList (for example). Here is an example of how you could do it:
case class ProductList (
list: Vector[Product]
)
object ProductList {
/**
* Mapping to and from JSON.
*/
implicit val documentFormatter: Format[ProductList] = (
(__ \ "products").format[Vector[Product]]
) (ProductList.apply, unlift(ProductList.unapply))
}
I am facing a problem in building the json message, i need a json message to be in the below format:
{
success:true,
count:3,
data: [
{id:1, data: SUCCESS},
{id:2, data: FAILURE},
{id:3, data: Not Declared}
]
}
Im anot sure how to do this plz help
You can do it the following way:
class Data { String id; String data }
def yourdata = [
new Data("1","SUCCESS")
new Data("2","FAILURE")
new ...
]
def builder = new groovy.json.JsonBuilder()
def root = builder {
success true
count 3
data yourdata.collect { d ->
["id":d.id,
"data":d.data]
}
}
return builder.toPrettyString()
I want to be able to parse a string to an object that I can access using the dot notation e.g. myobject.property, instead of the array notation e.g. myobject['property']. The array notation works fine. Here's what I have so far.
I have some XML:
<level1 name="level1name">
<level2 type="level2Type">
<entry>level2entry</entry>
<entry>level2entry</entry>
</level2>
</level1>
Which converts to the JSON:
{
"level1": {
"name": "level1name",
"level2": {
"type": "level2Type",
"entry": [
"level2entry",
"level2entry"
]
}
}
}
I have the following Dart code:
Object jsonObject = JSON.parse("""{
"level1": {
"name": "level1name",
"level2": {
"type": "level2Type",
"entry": [
"level2entry",
"level2entry"
]
}
}
}
""");
print("my test 1 == ${jsonObject}");
print("my test 2 == ${jsonObject['level1']}");
print("my test 3 == ${jsonObject['level1']['name']}");
which produce the (desired) output:
my test 1 == {level1: {name: level1name, level2: {type: level2Type, entry: [level2entry, level2entry]}}}
my test 2 == {name: level1name, level2: {type: level2Type, entry: [level2entry, level2entry]}}
my test 3 == level1name
But when I try:
print("my test 1 == ${jsonObject.level1}");
I get the following:
Exception: NoSuchMethodException : method not found: 'get:level1'
Receiver: {level1: {name: level1name, level2: {type: level2Type, entry: [level2entry, level2entry]}}}
Arguments: []
Stack Trace: 0. Function: 'Object.noSuchMethod' url: 'bootstrap' line:717 col:3
Ideally, I want an object that I can access using the dot notation and without the compiler giving warning about Object not having property. I tried the following:
class MyJSONObject extends Object{
Level1 _level1;
Level1 get level1() => _level1;
set level1(Level1 s) => _level1 = s;
}
class Level1 {
String _name;
String get name() => _name;
set name(String s) => _name = s;
}
...
MyJSONObject jsonObject = JSON.parse("""{
"level1": {
"name": "level1name",
"level2": {
"type": "level2Type",
"entry": [
"level2entry",
"level2entry"
]
}
}
}
""");
...
print("my test 1 == ${jsonObject.level1.name}");
but instead of giving me 'level1name' as hoped, I get:
Exception: type 'LinkedHashMapImplementation<String, Dynamic>' is not a subtype of type 'MyJSONObject' of 'jsonObject'.
What am I doing wrong here? Is there any way to do what I'm trying? Thanks.
At the moment, JSON.parse only returns Lists (array), Maps, String, num, bool, and null
(api ref).
I suspect that until reflection makes it way into the language, it won't be able to re-construct objects based upon the keys found in json.
You could, however, create a constructor in your MyJsonObject which took a string, called JSON.parse internally, and assigned the various values.
Something like this works in the dart editor:
#import("dart:json");
class Level2 {
var type;
var entry;
}
class Level1 {
var name;
var level2;
}
class MyJSONObject {
Level1 level1;
MyJSONObject(jsonString) {
Map map = JSON.parse(jsonString);
this.level1 = new Level1();
level1.name = map['level1']['name'];
level1.level2 = new Level2();
level1.level2.type = map['level1']['level2']['type'];
//etc...
}
}
main() {
var obj = new MyJSONObject(json);
print(obj.level1.level2.type);
}
A non trivial version would needs some loops and possible recursion if you had deeper nested levels.
Update: I've hacked together a non-trivial version (inspired by the post below), it's up on github (also taking Seth's comments re the constructor):
Chris is completely right. I will only add that the JSON parser could be modified to return a little richer object (something like JsonMap instead of pure Map) that could allow jsonObj.property by implementing noSuchMethod. That would obviously perform worse than jsonObj['property'].