I have developed a microservice using Spring Boot. The documentation for the REST API is made with Swagger. Some REST resources make use of Spring concepts to provide pagination for free. Below is an example:
#RequestMapping(value = "/buckets", method = GET)
public PagedResources list(Pageable pageable, PagedResourcesAssembler assembler) {
return bucketService.listBuckets(pageable, assembler);
}
If I open the Swagger page, the following form is available for the resource:
The issue I have is that the pageable parameter is detected with content-type application/json and I don't know how to pass a value to change the page size for example. All values seem to be ignored.
Is it possible to pass the query parameters as JSON object? or is it possible to configure Swagger to generate independent query parameter fields for getters contained by the Pageable interface?
Please note that I am using Springfox with Gradle:
compile 'io.springfox:springfox-spring-web:2.3.1'
compile 'io.springfox:springfox-swagger2:2.3.1'
compile 'io.springfox:springfox-swagger-ui:2.3.1'
This is a known issue with Spring-Fox. See Issue #755. Based on zdila's comment 2 at this time alternative is to add #ApiImplicitParams which is not ideal but it does work.
#ApiImplicitParams({
#ApiImplicitParam(name = "page", dataType = "integer", paramType = "query",
value = "Results page you want to retrieve (0..N)"),
#ApiImplicitParam(name = "size", dataType = "integer", paramType = "query",
value = "Number of records per page."),
#ApiImplicitParam(name = "sort", allowMultiple = true, dataType = "string", paramType = "query",
value = "Sorting criteria in the format: property(,asc|desc). " +
"Default sort order is ascending. " +
"Multiple sort criteria are supported.")
})
[
1 https://github.com/springfox/springfox/issues/755
2 https://github.com/springfox/springfox/issues/755#issuecomment-135059871
Building on Vineet Bhatia's answer, you can wrap the solution up in a custom annotation for reusability:
#Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE })
#Retention(RetentionPolicy.RUNTIME)
#ApiImplicitParams({
#ApiImplicitParam(name = "page", dataType = "int", paramType = "query", value = "Results page you want to retrieve (0..N)"),
#ApiImplicitParam(name = "size", dataType = "int", paramType = "query", value = "Number of records per page."),
#ApiImplicitParam(name = "sort", allowMultiple = true, dataType = "string", paramType = "query", value = "Sorting criteria in the format: property(,asc|desc). "
+ "Default sort order is ascending. " + "Multiple sort criteria are supported.") })
#interface ApiPageable {
}
Which can then be used like so:
#ApiPageable
public Page<Data> getData(Pageable pageRequest) {
Vineet Bhatia's answer with #ApiImplicitParams looks fine. But I faced with situation, when #ApiIgnor and #ApiParam(hidden = true) doesn't work and you can still observe the asembler and pageable params. I fixed this problem by adding next line
docket.ignoredParameterTypes(Pageable.class, PagedResourcesAssembler.class);
to the Docket bean in my SwaggerConfig.
Java example:
Bean:
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.paths(PathSelectors.any())
.build()
.directModelSubstitute(Pageable.class, SwaggerPageable.class);
}
SwaggerPageable:
#Getter
private static class SwaggerPageable {
#ApiParam(value = "Number of records per page", example = "0")
#Nullable
private Integer size;
#ApiParam(value = "Results page you want to retrieve (0..N)", example = "0")
#Nullable
private Integer page;
#ApiParam(value = "Sorting criteria in the format: property(,asc|desc). Default sort order is ascending. Multiple sort criteria are supported.")
#Nullable
private String sort;
}
Swagger:
Here is the version of the annotation that was integrated into springdoc-openapi-data-rest for OpenAPI v3:
#Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
#Retention(RetentionPolicy.RUNTIME)
#Parameter(in = ParameterIn.QUERY
, description = "Zero-based page index (0..N)"
, name = "page"
, content = #Content(schema = #Schema(type = "integer", defaultValue = "0")))
#Parameter(in = ParameterIn.QUERY
, description = "The size of the page to be returned"
, name = "size"
, content = #Content(schema = #Schema(type = "integer", defaultValue = "20")))
#Parameter(in = ParameterIn.QUERY
, description = "Sorting criteria in the format: property(,asc|desc). "
+ "Default sort order is ascending. " + "Multiple sort criteria are supported."
, name = "sort"
, content = #Content(array = #ArraySchema(schema = #Schema(type = "string"))))
public #interface PageableAsQueryParam {
}
See https://springdoc.github.io/springdoc-openapi-demos/faq.html#how-can-i-map-pageable-spring-date-commons-object-to-correct-url-parameter-in-swagger-ui
Open API 3.0 has seamless integration.
For example,
#GetMapping("/filter")
public Page<Employee> filterEmployees(Pageable pageable) {
return repository.getEmployees(pageable);
}
Add springdoc-openapi-data-rest dependency
implementation 'org.springdoc:springdoc-openapi-data-rest:1.5.2'
Note: You may add '#ParameterObject' if you have multiple parameters
public Page<Employee> filterEmployees(#ParameterObject Pageable pageable)
This solution works without the need to annotate every single API method in every single controller. First we create a replacement for Pageable class with the correct property names and descriptions (Kotlin code, you can use a Interface for Java):
data class SwaggerPageable(
#ApiModelProperty("Number of records per page", example = "20")
val size: Int?,
#ApiModelProperty("Results page you want to retrieve (0..N)", example = "0")
val page: Int?,
#ApiModelProperty("Sorting criteria in the format: property(,asc|desc)." +
"Default sort order is ascending. Multiple sort criteria are supported.")
var sort: String?
)
Then in the Swagger config, just add a direct substitute from Pageable to this class (again Kotlin code, but Java should be pretty similar):
#Bean
fun api(): Docket {
return Docket(DocumentationType.SWAGGER_2)
.select()
.paths(PathSelectors.any())
.build()
.directModelSubstitute(Pageable::class.java, SwaggerPageable::class.java)
}
The result looks like this:
The downside is not being able to define the default value in the ApiModelProperty, but this is more than good enough for my project.
Answer of Vineet Bhatia will have validation problem when you are not running on localhost. It will argue for integer parameters that they are not corresponding to json schema.
So I changed integer to string:
#ApiImplicitParams({
#ApiImplicitParam(name = "page", dataType = "string", paramType = "query",
value = "Results page you want to retrieve (0..N)"),
#ApiImplicitParam(name = "size", dataType = "string", paramType = "query",
value = "Number of records per page."),
#ApiImplicitParam(name = "sort", allowMultiple = true, dataType = "string", paramType = "query",
value = "Sorting criteria in the format: property(,asc|desc). " +
"Default sort order is ascending. " +
"Multiple sort criteria are supported.")
})
For people who wants to solve this problem in 2019. This configuration via springfox documentation works fine except you can't set description for parameters.
Code is here.
https://github.com/springfox/springfox/blob/ef1721afc4c910675d9032bee59aea8e75e06d27/springfox-data-rest/src/main/java/springfox/documentation/spring/data/rest/configuration/SpringDataRestConfiguration.java
here is a solution for springdoc-openapi-ui
SpringDocUtils.getConfig()
.replaceWithClass(org.springframework.data.domain.Pageable.class, SwaggerPageable.class);
#Getter
private static class SwaggerPageable {
#ApiParam(value = "Number of records per page", example = "0")
#Nullable
private Integer size;
#ApiParam(value = "Results page you want to retrieve (0..N)", example = "0")
#Nullable
private Integer page;
#ApiParam(value = "Sorting criteria in the format: property(,asc|desc). Default sort order is ascending. Multiple sort criteria are supported.")
#Nullable
private String sort;
}
Answer to validation problem indicated by Evgeny.
Using
#ApiImplicitParams({
#ApiImplicitParam(name = "page", dataType = "int", paramType = "query", value = "Results page you want to retrieve (0..N)"),
#ApiImplicitParam(name = "size", dataType = "int", paramType = "query", value = "Number of records per page."),
#ApiImplicitParam(name = "sort", allowMultiple = true, dataType = "string", paramType = "query", value = "Sorting criteria in the format: property(,asc|desc). "
+ "Default sort order is ascending. " + "Multiple sort criteria are supported.") })
throws an exception:
Illegal DefaultValue for parameter type integer
java.lang.NumberFormatException: For input string: ""
at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:68)
at java.base/java.lang.Long.parseLong(Long.java:709)
at java.base/java.lang.Long.valueOf(Long.java:1151)
at io.swagger.models.parameters.AbstractSerializableParameter.getExample(AbstractSerializableParameter.java:412)
at jdk.internal.reflect.GeneratedMethodAccessor366.invoke(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:567)
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:688)
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:719)
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:155)
at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serializeContents(IndexedListSerializer.java:119)
at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serialize(IndexedListSerializer.java:79)
(at least, it does with springfox-swagger2 and springfox-swagger2-ui version 2.9.2)
You can avoid the exception by following Evgeny's answer or by adding default values and example values for the integer parameters:
#ApiImplicitParams({
#ApiImplicitParam(name = "page", dataType = "int", paramType = "query", value = "Results page you want to retrieve (0..N)", defaultValue = "0", example = "2"),
#ApiImplicitParam(name = "size", dataType = "int", paramType = "query", value = "Number of records per page.", defaultValue = "20", example = "10"),
#ApiImplicitParam(name = "sort", allowMultiple = true, dataType = "string", paramType = "query", value = "Sorting criteria in the format: property(,asc|desc). "
+ "Default sort order is ascending. " + "Multiple sort criteria are supported.") })
An update:
Use org.springdoc.core.converters.models.DefaultPageable instead of Pageable. It already has the #ParameterObject which makes it breakdown in to 3 parameters when the Swagger JSON is generated.
Although the solution with the implicit parameters works, it introduces a lot of extra, brittle code. In the end we went with the following solution:
#GetMapping(value = "/")
public HttpEntity<PagedResources<Item>> getItems(
#RequestParam(value = "page", required = false) Integer page,
#RequestParam(value = "size", required = false) Integer size,
PagedResourcesAssembler assembler) {
Page<Item> itemPage = itemService.listItems(PageRequest.of(page, size, Sort.unsorted()));
return new ResponseEntity<>(assembler.toResource(itemPage), HttpStatus.OK);
}
We pass a PageRequest (which implements Pageable) to our service, which returns a Page. (all from org.springframework.data.domain).
The org.springframework.data.web.PagedResourcesAssembler gets injected automagically via the controller method and allows mapping Items to org.springframework.hateoas.PagedResources
We didn't require dynamic sorting so we omitted that; it poses some challenges to add sorting since springfox does not play nice with org.springframework.data.domain.Sort.
Related
I am trying to get financial data from Financial Modeling Prep's API into an excel spreadsheet. I am beginning to think that Power Query just does not do what I am looking for. I want to have one column with a static list of stock symbols (DAL, GOOG, AAL etc) and populate each row with financial data from various api calls such as the Net Income field from https://financialmodelingprep.com/api/v3/financials/income-statement/DAL and the current stock price from https://financialmodelingprep.com/api/v3/stock/real-time-price/DAL
What exactly have you tried? It's very simple to extract data from the first link you gave with the M Code below (all UI based, nothing advanced about that at all). Converting that into a function to go to the relevant URL for each code and do the same transformation is also trivial
let
Source = Json.Document(Web.Contents("https://financialmodelingprep.com/api/v3/financials/income-statement/DAL ")),
financials = Source[financials],
#"Converted to Table" = Table.FromList(financials, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
#"Expanded Column1" = Table.ExpandRecordColumn(#"Converted to Table", "Column1", {"date", "Net Income"}, {"date", "Net Income"}),
#"Changed Type" = Table.TransformColumnTypes(#"Expanded Column1",{{"Net Income", type number}, {"date", type date}})
in
#"Changed Type"
Here is my solution in Python:
Create parameters:
company = "NVDA"
years = 5
Add Key:
api_key = 'YOUR_KEY'
Request:
r = requests.get(f'https://financialmodelingprep.com/api/v3/income-statement/{company}?limit={years}&apikey={api_key}')
data = r.json()
data
Extract data
date = []
symbol = []
revenue = []
costOfRevenue = []
grossProfit = []
for finance in data:
date.append(finance["date"])
symbol.append(finance["symbol"])
revenue.append(finance["revenue"])
costOfRevenue.append(finance["costOfRevenue"])
grossProfit.append(finance["grossProfit"])
ncome_nvda_dict = {
"Date" : date,
"Ticket": symbol,
"Revenue" : revenue,
"CostOfRevenue" : costOfRevenue,
"grossProfit" : grossProfit,
From Object To Pands
income_nvda_df = pd.DataFrame(income_nvda_dict, columns = ['Date', 'Ticket', 'Revenue', 'CostOfRevenue', 'grossProfit'])
I'm extracting AVRO data which has a JSON field that I need to get values from. The JSON has an array, and I don't know what order the different elements of the array may appear in. How can I target specific node/values?
For example, Filters[0] could be Category one time, but could be AddressType another time.
I'm extracting AVRO data - i.e.
#rs =
EXTRACT date DateTime,
Body byte[]
FROM #input_file
USING new Microsoft.Analytics.Samples.Formats.ApacheAvro.AvroExtractor(#"
...
The Body is JSON that can look like this (but Category is not always Filter[0]. This is a small example; there are 7 different types of "Field"s):
{
""TimeStamp"": ""2019-02-19T15:00:29.1067771-05:00"",
""Filters"": [{
""Operator"": ""eq"",
""Field"": ""Category"",
""Value"": ""Sale""
}, {
""Operator"": ""eq"",
""Field"": ""AddressType"",
""Value"": ""Home""
}
]
}
My U-SQL looks like this, which of course does not always work.
#keyvalues =
SELECT JsonFunctions.JsonTuple(Encoding.UTF8.GetString(Body),
"TimeStamp",
"$.Filters[?(#.Field == 'Category')].Value",
"$.Filters[?(#.Field == 'AddressType')].Value"
) AS message
FROM #rs;
#results =
SELECT
message["TimeStamp"] AS TimeStamp,
message["Filters[0].Value"] AS Category,
message["Filters[1].Value"] AS AddressType
FROM #keyvalues;
Although this does not actually answer my question, as a workaround, I modified the Microsoft 'sample' JsonFunctions.JsonTuple method to be able to specify my own key name for extracted values:
/// Added - Prefix a path expression with a specified key. Use key~$e in the expression.
/// eg:
/// JsonTuple(json, "myId~id", "myName~name") -> field names MAP{ {myId, 1 }, {myName, Ed } }
The modified code:
private static IEnumerable<KeyValuePair<string, T>> ApplyPath<T>(JToken root, string path)
{
var keySeparatorPos = path.IndexOf("~");
string key = null;
var searchPath = path;
if (keySeparatorPos > 0) // =0?if just a leading "=", i.e. no key provided, then don't parse out a key.
{
key = path.Substring(0, keySeparatorPos).Trim();
searchPath = path.Substring(keySeparatorPos + 1);
}
// Children
var children = SelectChildren<T>(root, searchPath);
foreach (var token in children)
{
// Token => T
var value = (T)JsonFunctions.ConvertToken(token, typeof(T));
// Tuple(path, value)
yield return new KeyValuePair<string, T>(key ?? token.Path, value);
}
}
For example, I can access vales and name them
#keyvalues =
SELECT JsonFunctions.JsonTuple(Encoding.UTF8.GetString(Body),
"TimeStamp",
"EventName",
"Plan~ $.UrlParams.plan",
"Category~ $.Filters[?(#.Field == 'Category')].Value",
"AddressType~ $.Filters[?(#.Field == 'AddressType')].Value"
) AS message
FROM #rs;
#results =
SELECT
message["TimeStamp"] AS TimeStamp,
message["EventName"] AS EventName,
message["Plan"] AS Plan,
message["Category"] AS Category,
message["AddressType"] AS AddressType
FROM #keyvalues;
(I've not tested to see what would happen if the same Field appears multiple times in the array; That won't happen in my case)
I am learning M (Power Query Language). I'd like to use M to parse JSON from REST APIs. For example one can use the Stack Overflow REST API. I can see how to drill down into a simple JSON string using say
let
Source = Json.Document("{ ""glossary"": { ""title"": ""example glossary"", ""GlossDiv"": { ""title"": ""S"", ""GlossList"": { ""GlossEntry"":
{ ""ID"": ""SGML"", ""SortAs"": ""SGML"", ""GlossTerm"": ""Standard Generalized Markup Language"", ""Acronym"": ""SGML"",
""Abbrev"": ""ISO 8879:1986"", ""GlossDef"": { ""para"": ""A meta-markup language, used to create markup languages such as DocBook."",
""GlossSeeAlso"": [""GML"", ""XML""] }, ""GlossSee"": ""markup"" } } } } }"),
glossary = Source[glossary],
GlossDiv = glossary[GlossDiv],
GlossList = GlossDiv[GlossList],
GlossEntry = GlossList[GlossEntry],
ConvertedToTable = Record.ToTable(GlossEntry)
in
ConvertedToTable
But what happens when I have a list from which I want to drill in and fetch a subproperty and then I want to return all of those like a SQL UNION query. Actually it is more of a For Each type query.
So here is my non-working query that does not do a union but unfortunately glues the second record onto the side
let
Source = "{""items"":["
{""tags"":[""vba"",""permissions""],""owner"":
{""reputation"":49,""user_id"":9073241,""user_type"":""registered"",""accept_rate"":86,""display_name"":""Kam""},
""is_answered"":false,""view_count"":4,""answer_count"":0,""score"":0,""question_id"":48229549},
{""tags"":[""excel"",""vba"",""excel-vba""],""owner"":
{""reputation"":18,""user_id"":9057704,""user_type"":""registered"",""accept_rate"":29,""display_name"":""Gregory""},
""is_answered"":false,""view_count"":6,""answer_count"":0,""score"":0,""question_id"":48229590}
]}",
#"Parsed JSON" = Json.Document(Source),
items = #"Parsed JSON"[items],
item0 = items{0},
owner0 = item0[owner],
item1 = items{1},
owner1 = item1[owner],
#"Converted to Table" = Table.Combine( {Record.ToTable(owner0), Record.ToTable(owner1) })
in
#"Converted to Table"
What I am really aiming for is this output but not limited to 2 records, but all the records from the list. (The above sample source has been simplified from this REST API StackOverflow questions tagged VBA)
reputation user_id user_type accept_rate display_name
49 9073241 registered 86 Kam
18 9057704 registered 29 Gregory
I think you want to pivot your tables before you try to combine them. Try this query, for example.
let
Source1 = Json.Document("{""tags"":[""vba"",""permissions""],""owner"":
{""reputation"":49,""user_id"":9073241,""user_type"":""registered"",""accept_rate"":86,""display_name"":""Kam""},
""is_answered"":false,""view_count"":4,""answer_count"":0,""score"":0,""question_id"":48229549}"),
Owner1 = Table.Pivot(Record.ToTable(Source1[owner]), List.Distinct(Record.ToTable(Source1[owner])[Name]), "Name", "Value"),
Source2 = Json.Document("{""tags"":[""excel"",""vba"",""excel-vba""],""owner"":
{""reputation"":18,""user_id"":9057704,""user_type"":""registered"",""accept_rate"":29,""display_name"":""Gregory""},
""is_answered"":false,""view_count"":6,""answer_count"":0,""score"":0,""question_id"":48229590}"),
Owner2 = Table.Pivot(Record.ToTable(Source2[owner]), List.Distinct(Record.ToTable(Source2[owner])[Name]), "Name", "Value"),
#"Appended Query" = Table.Combine({Owner1, Owner2})
in
#"Appended Query"
If you just want to expand all of the owners, try a query more like this:
let
Source = "{""items"":[{""tags"":[""vba"",""permissions""],""owner"":{""reputation"":49,""user_id"":9073241,""user_type"":""registered"",""accept_rate"":86,""display_name"":""Kam""},""is_answered"":false,""view_count"":4,""answer_count"":0,""score"":0,""question_id"":48229549},{""tags"":[""excel"",""vba"",""excel-vba""],""owner"":{""reputation"":18,""user_id"":9057704,""user_type"":""registered"",""accept_rate"":29,""display_name"":""Gregory""},""is_answered"":false,""view_count"":6,""answer_count"":0,""score"":0,""question_id"":48229590}]}",
#"Parsed JSON" = Json.Document(Source),
items = #"Parsed JSON"[items],
#"Converted to Table" = Table.FromList(items, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
#"Expanded Column1" = Table.ExpandRecordColumn(#"Converted to Table", "Column1", {"owner"}, {"owner"}),
#"Expanded owner" = Table.ExpandRecordColumn(#"Expanded Column1", "owner", {"reputation", "user_id", "user_type", "display_name"}, {"reputation", "user_id", "user_type", "display_name"})
in
#"Expanded owner"
Just confused with one little thing.
I am currently getting { "data": [ { "response": "true" } ] } with the following code.
But I simple want to get { "response": "true" }.
I tried every way I can but I kept failing.
I will appreciate a lot if you can help me with it.
Set Dataset = JSON.parse("{ ""data"": [] }")
Set Record = JSON.parse("{}")
Record.set "response", "true"
Dataset.data.push(Record)
Set Record = nothing
Data = JSON.stringify(Dataset, null, 2)
Set Record = JSON.parse("{}")
Record.set "response", "true"
Data = JSON.stringify(Record, null, 2)
I assume that JSONObject.data.push adds the record to an unnamed array.
I'm new to the DataTables jquery plugin. After discovering that IE 8 had performance issues with Javascript I decided to change the way I use DataTables to do server side processing. I'm getting this error message when my JSP loads ( I'm using Spring 3 ):
DataTables warning (table id = 'results_table'): Requested unknown parameter '0' from the data source for row 0
I Googled around and found that many causes of that error message come down to malformed JSON so I found a way to output my JSON from my Spring 3 controller function to take a look at the JSON it makes and I changed my code to get it to be pretty close to what the DataTables site says it should look like.
Still no joy, still getting that error message.
The server side processing examples I found for DataTables didn't include code for specifying the columns used on the client side, so I assumed I don't need it. Do I?
Here are the relevant parts of my results.jsp:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
<title>ACME: search results in a nice DataTables.net Plugin</title>
</head>
<body>
<link rel="stylesheet" type="text/css" href="css/jquery.dataTables.css" />
<script language = "JavaScript" type = "text/javascript" src = "../nsd/js/jquery-1.7.js"></script>
<script language = "JavaScript" type = "text/javascript" src = "../nsd/js/jquery.dataTables.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$('#results_table').dataTable( {
"bProcessing": true,
"bServerSide": true,
"sScrollX": "600px",
"sServerMethod": "POST",
"sAjaxSource": "/acme/resultstable",
} );
} );
</script>
<form id="command" name="f" action="employee" method="post">
<div id = "results">
<table id = "results_table">
<thead>
<tr>
<th> </th>
<th>ID</th>
<th>NO_PRINT</th>
<th>Full Name</th>
<th>Email Address</th>
<th>Phone Number</th>
<th>Organization</th>
<th>Organization Code</th>
<th>Position</th>
<th>Employee Type</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</body>
</html>
Here is the JSON response I've been sending to it:
{
"sEcho" : 1,
"iTotalRecords" : 1,
"iTotalDisplayRecords" : 1,
"aaData" : [ {
"person_id" : "888888",
"ID" : "999999",
"no_print" : " ",
"fullname" : "Obama, Willard",
"email_address" : "barry#whitehouse.gov",
"current_phone_number" : "303-867-5309",
"title" : " ",
"office" : " ",
"position" : "Contractor",
"empl_code" : "CONT"
} ]
}
Here is my Spring controller function I am using to send the JSON response via Jackson. This includes code to output my JSON so I can see what it looks like. Could the JSON it outputs to stdout and what I am sending back to DataTables be different?
#RequestMapping(value = "/resultstable", method = RequestMethod.POST)
public #ResponseBody LinkedHashMap resultstable(ModelMap model,
HttpSession session,
#RequestParam (required=true) int sEcho,
#RequestParam (required=true) int iDisplayStart,
#RequestParam (required=true) int iDisplayLength,
#RequestParam (required=true) int iColumns,
#RequestParam (required=true) int iSortCol_0,
#RequestParam (required=false)String sSortDir_0,
#RequestParam (required=true) String sSearch ) {
/*
**********************************************************************
** These come from the DataTables.net Jquery plugin on results.jsp
**********************************************************************
** sEcho, - just send it back, used by DataTables for synching
** iDisplayStart - index of the record to start with, ie 3 for the 3rd of 100 records
** iDisplayLength - number of records to send back starting with iDisplayStart
** iColumns - number of columns to be displayed in the table
** iSortCol_0 - the number of thee column to be sorted on
** sSortDir_0 - direction of sorting: asc or desc
** sSearch - from the search box, filter results further on this term
**********************************************************************
*/
String nextView = "results";
String usertype = (String)session.getAttribute("usertype");
Search search = new Search(usertype);
List<LinkedHashMap> records = null;
String results = null;
int number_of_records = (Integer)session.getAttribute("number_of_records_found");
ResultsView rv = new ResultsView();
ResultsScreenTableHolder rstrh = null;
SearchScreenDataHolder ssdh2 = (SearchScreenDataHolder)session.getAttribute("search_screen_data_holder");
ObjectMapper mapper = new ObjectMapper();
logger.debug("started");
logger.debug("sEcho, == " + sEcho );
logger.debug("iDisplayStart == " + iDisplayStart );
logger.debug("iDisplayLength == " + iDisplayLength );
logger.debug("iColumns == " + iColumns );
logger.debug("iSortCol_0 == " + iSortCol_0 );
logger.debug("sSortDir_0 == " + sSortDir_0 );
logger.debug("sSearch == " + sSearch );
try {
records = search.searchForAnEmployee(ssdh2,usertype,sSearch,"asc",
iSortCol_0,iDisplayStart,
iDisplayLength);
LinkedHashMap lhm= new java.util.LinkedHashMap();
lhm.put("sEcho", sEcho);
lhm.put("iTotalRecords",number_of_records);
lhm.put("iTotalDisplayRecords",9);
lhm.put("aaData",records);
// convert user object to json string, and save to a file
mapper.writeValue(new File("c:\\Downloads\\rstrh.json.txt"), lhm);
// display to console
logger.debug("My JSON: " + mapper.defaultPrettyPrintingWriter().writeValueAsString(lhm));
}
catch (Exception e) {
logger.debug("\n",e);
}
return lhm;
}// end function
I ran into the same warning as well, but the cause was different. I had null values in my data. The JSON format was correct, but DataTables does not know have a default rule for displaying nulls. The solution was to use the sDefaultContent property.
Sample aaData:
aaData: [
{ "Field1": "Foo", "Field2":null },
{ "Field1": "Bar", "Field2":null },
]
And then on the aoColumns, you can use the property as follows:
aoColumns: [
{ "mData": "Field1", sDefaultContent: "n/a" },
{ "mData": "Field2", sDefaultContent: "" }
]
This is not your current problem, but you may encounter this issue in the future.
Hope this was helpful.
I was having this same problem this morning. You need to have the aoColumns parameter and use mDataProp As in this:
https://gist.github.com/1660712
At least it solved my problem.
sDefaultContent option prevents displaying alert boxes only. Data tables wil not find the rule for displaying null values.
Changing null values in the table will eliminate this warning..
If it helps anyone I had a similar error because I had periods in my mRender function name:
My.Namespace.MyFunction(data, row);
This line:
var a = _fnSplitObjNotation( src );
Splits that into separate objects, which obviously generates an error.
Using
My_Namespace_MyFunction(data, row);
Additionally I noticed this error when passing a string function name instead of the JavaScript function object.
To avoid this error, your table number of "th" columns should equal to returning data columns(in numbers), in the above problem it is aaData.
"aaData" : [
[
"person_id" : "888888",
"ID" : "999999",
],
[
"person_id" : "8888889",
"ID" : "9999990",
]
]
This is correct format to return data from server side language.
I have solved my problem in same way.