I am trying to return JSON of SOLRDocList type which is :
{numFound=0,start=0,docs=[]}{"start":0,"empty":true,"class":"class org.apache.solr.common.SolrDocumentList","numFound":0}
However since I am using Service class as :
#POST
#Path("/userQuery")
#Consumes(MediaType.TEXT_PLAIN)
#Produces(MediaType.APPLICATION_JSON)
public SolrDocumentList userQuery(String p){
int sizy;
String stry;
StringBuilder sb = new StringBuilder();
SolrDocument doc = null;
SolrDocumentList docList = null;
List<String> arr = new ArrayList();
StringTokenizer token = new StringTokenizer(p,",");
while(token.hasMoreElements()){
stry = token.nextToken();
arr.add(stry);
}
Set<String> xrr = new HashSet<String>(arr);
SolrQuery query = new SolrQuery(createQuery(arr));
query.setIncludeScore(false);
query.setFields("country_code,last_name,raw_location");
query.setParam("wt", "json");
System.out.println(query);
QueryResponse qr = authorSearcher.query(query);
docList = qr.getResults();
return docList;
}
Error is :
SEVERE: A message body writer for Java class org.apache.solr.common.SolrDocumentList, and Java type class org.apache.solr.common.SolrDocumentList, and MIME media type application/json was not found
SEVERE: The registered message body writers compatible with the MIME media type are:
application/json ->
com.sun.jersey.json.impl.provider.entity.JSONJAXBElementProvider$App
com.sun.jersey.json.impl.provider.entity.JSONArrayProvider$App
com.sun.jersey.json.impl.provider.entity.JSONObjectProvider$App
com.sun.jersey.json.impl.provider.entity.JSONRootElementProvider$App
com.sun.jersey.json.impl.provider.entity.JSONListElementProvider$App
*/* ->
I want to know if there is any possible way to convert the SOLRDocList type which is JSON into the supported type to resolve this issue?
Thanks for your time !
I figured this out, since I am using solr4j , this library doesn't support JSON as a output , in such situation using external library or :
Map<Integer,Object> solrDocMap = new HashMap<Integer,Object>();
int counter = 1;
for(Map singleDoc: docList)
{
solrDocMap.put(counter, new JSONObject(singleDoc));
counter++;
}
try {
returnResults.put("docs",solrDocMap);
}
and when you want to return a JSON string instead going with 3rd party libraries , go with something like:
String x = ""+returnResults;
return x;
Related
I have a C# Asp.Net MVC (5.2.7) app with support for WebApi 2.x targeting .Net 4.5.1.
I am experimenting with F# and I added an F# library project to the solution. The web app references the F# library.
Now, I want to be able to have the C# WebApi controller return F# objects and also save F# objects. I have trouble serializing a F# record with an Option field. Here is the code:
C# WebApi controller:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using FsLib;
namespace TestWebApp.Controllers
{
[Route("api/v1/Test")]
public class TestController : ApiController
{
// GET api/<controller>
public IHttpActionResult Get()
{
return Json(Test.getPersons);
}
// GET api/<controller>/5
public string Get(int id)
{
return "value";
}
[AllowAnonymous]
// POST api/<controller>
public IHttpActionResult Post([FromBody] Test.Person value)
{
return Json(Test.doSomethingCrazy(value));
}
// PUT api/<controller>/5
public void Put(int id, [FromBody]string value)
{
}
// DELETE api/<controller>/5
public void Delete(int id)
{
}
}
}
FsLib.fs:
namespace FsLib
open System.Web.Mvc
open Option
open Newtonsoft.Json
module Test =
[<CLIMutable>]
//[<JsonConverter(typeof<Newtonsoft.Json.Converters.IdiomaticDuConverter>)>]
type Person = {
Name: string;
Age: int;
[<JsonConverter(typeof<Newtonsoft.Json.FSharp.OptionConverter>)>]
Children: Option<int> }
let getPersons = [{Name="Scorpion King";Age=30; Children = Some 3} ; {Name = "Popeye"; Age = 40; Children = None}] |> List.toSeq
let doSomethingCrazy (person: Person) = {
Name = person.Name.ToUpper();
Age = person.Age + 2 ;
Children = if person.Children.IsNone then Some 1 else person.Children |> Option.map (fun v -> v + 1); }
let deserializePerson (str:string) = JsonConvert.DeserializeObject<Person>(str)
Here is the OptionConverter:
namespace Newtonsoft.Json.FSharp
open System
open System.Collections.Generic
open Microsoft.FSharp.Reflection
open Newtonsoft.Json
open Newtonsoft.Json.Converters
/// Converts F# Option values to JSON
type OptionConverter() =
inherit JsonConverter()
override x.CanConvert(t) =
t.IsGenericType && t.GetGenericTypeDefinition() = typedefof<option<_>>
override x.WriteJson(writer, value, serializer) =
let value =
if value = null then null
else
let _,fields = FSharpValue.GetUnionFields(value, value.GetType())
fields.[0]
serializer.Serialize(writer, value)
override x.ReadJson(reader, t, existingValue, serializer) =
let innerType = t.GetGenericArguments().[0]
let innerType =
if innerType.IsValueType then typedefof<Nullable<_>>.MakeGenericType([|innerType|])
else innerType
let value = serializer.Deserialize(reader, innerType)
let cases = FSharpType.GetUnionCases(t)
if value = null then FSharpValue.MakeUnion(cases.[0], [||])
else FSharpValue.MakeUnion(cases.[1], [|value|])
I want to serialize the Option field to the value, if it is not None and to null if it is None. And vice-versa, null -> None, value -> Some value.
The serialization works fine:
[
{
"Name": "Scorpion King",
"Age": 30,
"Children": 3
},
{
"Name": "Popeye",
"Age": 40,
"Children": null
}
]
However, when I post to the url, Person parameter is serialized to null and the ReadJson method is not invoked. I used Postman (the chrome app) to post by selecting the Body -> x-www-form-urlencoded. I set up three parameters: Name=Blob,Age=103,Children=2.
In WebApiConfig.cs I have:
config.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new Newtonsoft.Json.FSharp.OptionConverter());
However, if I just have this and remove the JsonConverter attribute from the Children field, it doesn't seem to have any effect.
This what gets sent to the server:
POST /api/v1/Test HTTP/1.1
Host: localhost:8249
Content-Type: application/x-www-form-urlencoded
Cache-Control: no-cache
Postman-Token: b831e048-2317-2580-c62f-a00312e9103b
Name=Blob&Age=103&Children=2
So, I don't know what's wrong, why the deserializer converts the object to null. If I remove the option field it works fine. Also if I remove the Children field from the payload it works fine as well. I do not understand why the ReadJson method of the OptionCoverter is not invoked.
Any ideas?
Thanks
An update: In the comments it was rightly pointed I did not post a application/json payload. I did it and the serialization still doesn't work properly.
Update 2:
After more testing, this works:
public IHttpActionResult Post(/*[FromBody]Test.Person value */)
{
HttpContent requestContent = Request.Content;
string jsonContent = requestContent.ReadAsStringAsync().Result;
var value = Test.deserializePerson(jsonContent);
return Json(Test.doSomethingCrazy(value));
}
The following is the Linqpad code I used for testing the request (I didn't use postman):
var baseAddress = "http://localhost:49286/api/v1/Test";
var http = (HttpWebRequest) WebRequest.Create(new Uri(baseAddress));
http.Accept = "application/json";
http.ContentType = "application/json";
http.Method = "POST";
string parsedContent = "{\"Name\":\"Blob\",\"Age\":100,\"Children\":2}";
ASCIIEncoding encoding = new ASCIIEncoding();
Byte[] bytes = encoding.GetBytes(parsedContent);
Stream newStream = http.GetRequestStream();
newStream.Write(bytes, 0, bytes.Length);
newStream.Close();
var response = http.GetResponse();
var stream = response.GetResponseStream();
var sr = new StreamReader(stream);
var content = sr.ReadToEnd();
content.Dump();
Update 3:
This works just fine - i.e. I used a C# class:
public IHttpActionResult Post(/*[FromBody]Test.Person*/ Person2 value)
{
// HttpContent requestContent = Request.Content;
// string jsonContent = requestContent.ReadAsStringAsync().Result;
//
// var value = Test.deserializePerson(jsonContent);
value.Children = value.Children.HasValue ? value.Children.Value + 1 : 1;
return Json(value);//Json(Test.doSomethingCrazy(value));
}
public class Person2
{
public string Name { get; set; }
public int Age { get; set; }
public int? Children { get; set; }
}
I found a fix, based on this post: https://stackoverflow.com/a/26036775/832783.
I added in my WebApiConfig Register method this line:
config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new DefaultContractResolver();
I still have to investigate what's happening here.
Update 1:
It turns out that calling Json(...) in the ApiController method creates a new JsonSerializerSettings object instead of using the default set in the global.asax. What this means is that any converters added there don't have any effect on the serialization to json when the Json() result is used.
Just to throw another option in, if you're willing to upgrade the framework version you could use the new System.Text.Json APIs which should be nice and fast, and the FSharp.SystemTextJson package gives you a custom converter with support for records, discriminated unions etc.
I know there are other posts similar to this, but I haven't found any that help me find a solution for this particular case.
I am trying to return a HashMap<String, Object> from my Controller.
The Object part is a JSON string, but its being double serialized and not returned as a raw JSON string, thus not ending up with extra quotations and escape characters.
Controller function:
#RequestMapping(method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public HashMap<String, Object> heartbeat(){
String streamInfo = service.getStreamInfo();
String streamCursorInfo = service.getStreamCursorInfo();
String topicInfo = service.getTopicInfo();
String greeting = "This is a sample app for using Spring Boot with MapR Streams.";
HashMap<String, Object> results = new HashMap();
results.put("greeting", greeting);
results.put("streamInfo", streamInfo);
results.put("streamCursorInfo", streamCursorInfo);
results.put("topicInfo", topicInfo);
return results;
}
Service function:
private String performCURL(String[] command){
StringBuilder stringBuilder = new StringBuilder();
try{
ProcessBuilder processBuilder = new ProcessBuilder(command);
Process p = processBuilder.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = null;
while((line = reader.readLine()) != null){
stringBuilder.append(line);
}
}
catch(Exception e){
LOGGER.error(ExceptionUtils.getRootCauseMessage(e));
}
return stringBuilder.toString();
}
The cURL command I run already returns a raw JSON string. So im just trying to add it to the HashMap to be returned in the heartbeat response.
But every time I run this, my output looks like:
{
"greeting": "This is a sample app for using Spring Boot with MapR Streams.",
"streamCursorInfo": "{\"timestamp\":1538676344564,\"timeofday\":\"2018-10-04 02:05:44.564 GMT-0400 PM\",\"status\":\"OK\",\"total\":1,\"data\":[{\"consumergroup\":\"MapRDBConsumerGroup\",\"topic\":\"weightTags\",\"partitionid\":\"0\",\"produceroffset\":\"44707\",\"committedoffset\":\"10001\",\"producertimestamp\":\"2018-10-03T05:57:27.128-0400 PM\",\"consumertimestamp\":\"2018-09-21T12:35:51.654-0400 PM\",\"consumerlagmillis\":\"1056095474\"}]}",
...
}
If i return only the single string, such as streamInfo then it works fine and doesnt add the extra quotes and escape chars.
Can anyone explain what im missing or need to do to prevent this double serialization?
Instead of returning a HashMap, create an object like this:
public class HeartbeatResult {
private String greeting;
... //other fields here
#JsonRawValue
private String streamCursorInfo;
... //getters and setters here (or make the object immutable by having just a constructor and getters)
}
With #JsonRawValue Jackson will serialize the string as is. See https://www.baeldung.com/jackson-annotations for more info.
streamCursorInfo is a string, not an object => the serialization will escape the " character.
If you are able to return the object containing the data, it will work out of the box. If what you have is just a String, I suggest to serialize it to JsonNode and add it in your response
ObjectMapper objectMapper = new ObjectMapper();
JsonNode streamCursorInfo = objectMapper.readTree(service.getStreamInfo())
results.put("streamCursorInfo", streamCursorInfo);
I am deserializing a JSON http response and reading it into a Java object.
My first solution was:
javax.ws.rs.core.Response response = ...
MyClass obj = response.readEntity(MyClass.class);
I want to read unknown enum values of enum members in MyClass as null and not throw an exception, so I modified the above to:
String str = response.readEntity(String.class);
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL, true);
MyClass obj = mapper.readValue(string, Class<MyClass>);
Does anyone know how I could avoid the double deserialization here i.e. instead of Response -> String -> Class, just do Response -> Class?
So the solution to this is to register an ObjectMapperContextResolver with the client
private final ObjectMapperContextResolver OBJECT_MAPPER_CONTEXT_RESOLVER = new ObjectMapperContextResolver();
javax.ws.rs.client.Client client = ...
javax.ws.rs.client.WebTarget target = client.target("...");
client.register(OBJECT_MAPPER_CONTEXT_RESOLVER).target("...");
javax.ws.rs.core.Response response = target.path("...").request(MediaType.APPLICATION_JSON).get();
MyClass obj = response.readEntity(MyClass.class);
I was using Jersey 1.16 to consume a JSON, but now I'm with difficulties to consume a JSON using Jersey 2.0 (that implements JAX-RS 2.0).
I have a JSON response like this:
{
"id": 105430,
"version": 0,
"cpf": "55443946447",
"email": "maria#teste.br",
"name": "Maria",
}
and the method that consumes it:
public static JSONObject get() {
String url = "http://127.0.0.1:8080/core/api/person";
URI uri = URI.create(url);
final Client client = ClientBuilder.newClient();
WebTarget webTarget = client.target(uri);
Response response = webTarget.request(MediaType.APPLICATION_JSON).get();
if (response.getStatus() == 200) {
return response.readEntity(JSONObject.class);
}
}
I also tried:
return webTarget.request(MediaType.APPLICATION_JSON).get(JSONObject.class);
But the jSONObject return is null. I don't understand my error because the response is OK!
This is how to use the Response type correctly:
private void getRequest() {
Client client = ClientBuilder.newClient();
String url = "http://localhost:8080/api/masterdataattributes";
WebTarget target = client.target(url);
Response res = target
.request(MediaType.APPLICATION_JSON)
.get();
int status = res.getStatus();
String json = res.readEntity(String.class);
System.out.println(String.format("Status: %d, JSON Payload: %s", status, json));
}
If you're just interested in the payload, you could also just issue a get(String.class). But usually you will also want to check the response status, so working with the Response is usually the way to go.
If you want a typed (generic) JSON response, you could also have readEntity return a Map, or a list of Map if the response is an array of objects as in this example:
List<Map<String, Object>> json = res.readEntity(new GenericType<List<Map<String, Object>>>() {});
String id = (String) json.get(0).get("id");
System.out.println(id);
I have found the solution. Maybe it is not the best of, but it works.
public static JsonObject get() {
String url = "http://127.0.0.1:8080/core/api/person";
URI uri = URI.create(url);
final Client client = ClientBuilder.newClient();
WebTarget webTarget = client.target(uri);
Response response = webTarget.request(MediaType.APPLICATION_JSON).get();
//Se Response.Status.OK;
if (response.getStatus() == 200) {
StringReader stringReader = new StringReader(webTarget.request(MediaType.APPLICATION_JSON).get(String.class));
try (JsonReader jsonReader = Json.createReader(stringReader)) {
return jsonReader.readObject();
}
}
return null;
}
I switched the class JSONObject (package import org.codehaus.jettison) by JsonObject (package javax.json) and I used the methods to manipulate the content as String.
S.
mmey answer is the correct and optimal one, instead of invoking the service twice it does it one time.
I'm using JPA Toplink, JAX-RS, NetBean6.9
So far I successfully convert JPQL query result which is List with one object type into JSON.
Following works fine, it generates JSON by the time it gets to the browser because I specified so in JPA annotation
JPA snippet
#XmlRootElement //here enable JSON
#Entity
#Table(name = "MasatosanTest")
Resource class snippet
#Path("allJoin")
#GET
#Produces("application/json")
public List<MasatosanTest> getAllJoinResult() {
EntityManager em = null;
List<Object[]> mt = null;
try {
em = EmProvider.getDefaultManager();
Query query = em.createQuery("SELECT m1, m2 FROM MasatosanTest m1, MasatosanTest2 m2");
mt = query.getResultList();
}
catch(Exception e) {
System.out.println("MasatosanTestResource.java - getJoinedResult ERROR: " + e);
}
finally {
if(em != null) {
em.close();
}
}
//I'm only adding MasatosanTest object into the List not MasatosanTest2
List<MasatosanTest> list = new ArrayList<MasatosanTest>();
MasatosanTest mt = null;
for(Object[] items : mt) {
mt = (MasatosanTest)items[0];
list.add(mt);
}
return list;
}//end getAllJoinResult()
The code above in the browser will output something like:
[MasatosanTest : [[name:foo], [name:moo]]
My problem is when 2 different Object types are returned by JPQL, it won't convert to JSON automatically anymore.
If I modify the code above a bit where I'm adding MasatosanTest2 to list as well.
//Now I'm adding MasatosanTest2 to the List in addition to MasatosanTest
//So changing List data type to List<Object> to accept both object types.
List<Object> list = new ArrayList<Object>();
MasatosanTest mt = null;
MasatosanTest2 mt2 = null;
for(Object[] items : mt) {
mt = (MasatosanTest)items[0];
mt2 = (MasatosanTest2)items[1];
list.add(mt);
list.add(mt2)
}
return list;
Then of course change method return type to List too.
public List<Object> getAllJoinResult() {
Then I get an error complaining it can't be JSON :(
A message body writer for Java type, class java.util.ArrayList,
and MIME media type, application/json, was not found
Is it allowed to have JSON that contains multiple types?
My goal is to have JSON like:
[[MasatosanTest : [[name:foo], [name:moo]],
[MasatosanTest2 : [[name:boo], [name:www]] ]
After testing few based on other people's help online, I figured that returned query result is List<Object[]> and this Object[] is actually Vector.
So, below would do the job (not sure if efficient enough though)
Also JSONStringer is from jettison api.
List<Object[]> out = null;
JSONStringer jstr = null;
sb = new StringBuilder();
Vector vec = null;
for(Object item : out) {
vec = (Vector)item;
jstr = new JSONStringer();
String json = jstr.object()
.key("columnName1").value( vec.get(0) )
.key("columnName2").value( vec.get(1) )
.key("columnName3").value( vec.get(2) )
.key("columnName4").value( vec.get(3) )
.key("columnName5").value( vec.get(4) )
.endObject().toString();
sb.append(json).append(",");
jstr = null;//reset
}
sb.deleteCharAt(sb.length() - 1);
System.out.println("====== json out result =====> " + sb.toString());