Is it possible to change Json field name two times in Spring rest api. I know it is not very meaningful but I need something like this.
For example the json which I am getting from the remote service is
{
total_count : 1;
}
My Model class is like
public class Model
{
#JsonProperty("total_count")
int count;
}
And from my rest service I want to return a json of Model class but with the field "count" instead of "total_count"
{
count: 1
}
Is that possible to do something like this?
Try something like:
public class Model {
int count;
#JsonGetter("count")
public int getCount() {
return count;
}
#JsonSetter("total_count")
public void setCount(int count) {
this.count = count;
}
}
If you do not want to disturb the pojo classes then you can follow below solution to format the json solution and send response.
On JSONObject do below.
obj.put("count", obj.get("total_count"));
obj.remove("total_count");
Related
I'm kind of a fresher when it comes to doing API work especially with JSON.
Here's what my code looks like...
Endpoing:
[HttpPost("postWithBody")]
public async Task<IActionResult> PostWithBody (string param1, [FromBody] object requestBody)
{
var x = JsonSerializer.Deserialize<ParamModel>(requestBody); <-- Error cannot convert from 'object' to System.ReadOnlySpan<byte>
return ok(param1); <--this here just so it doesn't bark at me
}
SO in the above code, I'm erroring out on (RequestBody) with this error:
Error cannot convert from 'object' to System.ReadOnlySpan
public class ParamModel
{
public string PName {get;set;}
public string PValue {get;set;}
}
But essentially to finish the demo of what I'm trying to accomplish is, goal is to pass JSON value to this endpoint in the body that looks like this:
{
"Param1": "XXX",
"Param2": "111"
}
and my goal would be for CustomParams model class to have the
PName = Param1
PValue = "XXX"
and
PName = "Param2"
PValue = "111"
Is this the correct approach I'm taking?
Thank you.
EDIT: I guess I could do something like: [FromBody] ParamModel requestBody
and I did try it, when I pass JSON like this, it came as null in the endpoint:
{"test":"hey"}
But also, I probably would need to do something like this, since I want to have the option of passing multiple params.
public class ParamList
{
public List<ParamModel> data {get;set;}
}
and have that be [FromBody] ParamList requestBody
First of all I would suggest that you use the model in the action parameter and let the framework do the deserialisation for you:
public async Task<IActionResult> PostWithBody(
string param1, [FromBody] ParamModel requestBody)
Now you will be able to post JSON in that matches that object, something like this for example:
{
"PName": "test",
"PValue": "hey"
}
In your update, you say that you would like instead to use the ParamList object. In that case, you would need JSON that matches, something like this:
{
"data": [
{ "PName": "test1", "PValue": "hey1" },
{ "PName": "test2", "PValue": "hey2" }
]
}
Now in your action you can loop over the list like this:
foreach(var param in requestBody.data)
{
var paramName = param.PName;
var paramValue = param.PValue;
// etc.
}
I want to test a contract where one field is of type java.time.Instant. But not all instances of an Instant are handled as I expect by spring-cloud-contract. Given the following simple contract:
Contract.make {
description("Get a version")
request {
method 'GET'
url '/config/version'
headers {
contentType(applicationJson())
}
}
response {
status 200
body(
nr: 42,
creationDate: producer(anyIso8601WithOffset())
)
headers {
contentType(applicationJson())
}
}
}
And this service implementation:
#RestController
public class VersionController {
#GetMapping(path = "/version")
public ResponseEntity<Version> getCurrentVersion() {
return ResponseEntity.ok(new Version(42, Instant.ofEpochMilli(0)));
}
}
Executing gradle test works fine. But if I replace the Instant with Instant.now(), my provider test fails with
java.lang.IllegalStateException: Parsed JSON [{"nr":42,"creationDate":"2018-11-11T15:28:26.958284Z"}] doesn't match the JSON path [$[?(#.['creationDate'] =~ /([0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(\.\d{3})?(Z|[+-][01]\d:[0-5]\d)/)]]
which is understandable because Instant.now() produces an Instant whose string representation does indeed not match the anyIso8601WithOffset() pattern. But why is this? Why are Instants represented differently and how can I describe a contract that validates for any instant?
Ok, I found a solution that works for me. Although I do not know if this is the way to go.
In order to always get the exact same format of the serialized instant, I define the format of the corresponding property of my version bean as follows:
public class Version {
private final int nr;
private final Instant creationDate;
#JsonCreator
public Version(
#JsonProperty("nr") int nr,
#JsonProperty("creationDate") Instant creationDate)
{
this.nr = nr;
this.creationDate = creationDate;
}
public int getNr() {
return nr;
}
#JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSX", timezone = "UTC")
public Instant getCreationDate() {
return creationDate;
}
}
I have Created two java classes TestA.java,TestB.java using restAssured where each of the class reads json from TestA.json and testB.json and post a request to endpoint uri.TestA.java returns a json response having tag "customerID" which will be input for one of the tags of TestB.json and when ever I post a request using "TestB.java" customerID has to be picked from TestB.json .How do my code look like?Any ideas?
My code :
TestA.java
String requestBody = generateString("CreateCustomer.json");
RestAssured.baseURI = "https://localhost:8080";
Response res = given().contentType(ContentType.JSON)
.header("Content-Type", "application/json").header("checkXML", "N").body(requestBody).when()
.post("/restservices/customerHierarchy/customers").then().assertThat()
.statusCode(201).and().body("transactionDetails.statusMessage", equalTo("Success")).and().log().all()
.extract().response();
//converts response to string
String respose = res.asString();
JsonPath jsonRes = new JsonPath(respose);
CustomerID = jsonRes.getString("customerNodeDetails.customerId");
TestA.java response
{
"customerNodeDetails": {
"customerId": "81263"
},
Now i want to pass this customerID as input in testB.json or testB.java which is dynamic.
TestB.json
"hierarchyNodeDetails": {
"**customerId**":"",
"accountNumber": "",
"startDate": "",
}
Both TestA.java and TestB.java looks almost same except the post uri
Thanks in Advance
It depends on how you are distributing your classes:
If you want to write the tests for A and B in a single class. Declare a local variable of type Response/String and then store the customer ID in that variable. The scope of the variable will be live in all TestNG methods. You can set the customer ID for the B.json from the local variable.
public class Test{
String _cust_id;
#Test
public void test_A(){
// ceremony for getting customer id
_cust_id = jsonRes.getString("customerNodeDetails.customerId");
}
#Test
public void test_B(){
// write value of customer id using the variable _cust_id
}}
You can try this approach, but would suggest separating the data part to a dataProvider class.
If you want to have separate classes for A and B, use ITestContext to pass values from one class to the other.
public class A{
#Test
public void test1(ITestContext context){
context.setAttribute("key", "value");
}
}
public class B{
#Test
public void test2(ITestContext context){
String _attribute = context.getAttribute(key);
}
}
The elegant way could be, use a dataProvider for class B test where you perform the ceremony of getting the customerID from class A Tests.
public class DataB{
#DataProvider
public static Object[][] _test_data_for_b(){
// get the customerID
// create the json for class B
// return the json required for class B test
// All these could be achieved as everything has a common scope
}}
public class B{
#Test(dataProvider = "_test_data_for_b", dataProviderClass = DataB.class)
public void test_B(String _obj){
// perform the testing
}}
I need to replace the DateTime serialization for JSON in WCF REST Self Hosted service. Right now, I'm using something like the following code to do it, but it's definitely not the way to go since it requires manipulating each class.
[DataContract]
public class Test
{
[IgnoreDataMember]
public DateTime StartDate;
[DataMember(Name = "StartDate")]
public string StartDateStr
{
get { return DateUtil.DateToStr(StartDate); }
set { StartDate = DateTime.Parse(value); }
}
}
where my utility function DateUtil.DateToStr does all the formatting work.
Is there any easy way to do it without having to touch the attributes on my classes which have the DataContract attribute? Ideally, there would be no attributes, but a couple of lines of code in my configuration to replace the serializer with one where I've overridden DateTime serialization.
Everything that I've found looks like I have to replace huge pieces of the pipeline.
This article doesn't appear to apply because in I'm using WebServiceHost not HttpServiceHost, which not part of the 4.5.1 Framework.
JSON.NET Serializer for WCF REST Services
By default WCF uses DataContractJsonSerializer to serialize data into JSON. Unfortunatelly date from this serializer is in very difficult format to parse by human brain.
"DateTime": "\/Date(1535481994306+0200)\/"
To override this behavior we need to write custom IDispatchMessageFormatter. This class will receive all data which should be returned to requester and change it according to our needs.
To make it happen to the operations in the endpoint add custom formatter - ClientJsonDateFormatter:
ServiceHost host=new ServiceHost(typeof(CustomService));
host.AddServiceEndpoint(typeof(ICustomContract), new WebHttpBinding(), Consts.WebHttpAddress);
foreach (var endpoint in host.Description.Endpoints)
{
if (endpoint.Address.Uri.Scheme.StartsWith("http"))
{
foreach (var operation in endpoint.Contract.Operations)
{
operation.OperationBehaviors.Add(new ClientJsonDateFormatter());
}
endpoint.Behaviors.Add(new WebHttpBehavior());
}
}
ClientJsonDateFormatter is simple class which just applies formatter ClientJsonDateFormatter
public class ClientJsonDateFormatter : IOperationBehavior
{
public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters) { }
public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation) { }
public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
{
dispatchOperation.Formatter = new ResponseJsonFormatter(operationDescription);
}
public void Validate(OperationDescription operationDescription) { }
}
In the formatter we took imput and serialize it with the changed Serializer:
public class ResponseJsonFormatter : IDispatchMessageFormatter
{
OperationDescription Operation;
public ResponseJsonFormatter(OperationDescription operation)
{
this.Operation = operation;
}
public void DeserializeRequest(Message message, object[] parameters)
{
}
public Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result)
{
string json=Newtonsoft.Json.JsonConvert.SerializeObject(result);
byte[] bytes = Encoding.UTF8.GetBytes(json);
Message replyMessage = Message.CreateMessage(messageVersion, Operation.Messages[1].Action, new RawDataWriter(bytes));
replyMessage.Properties.Add(WebBodyFormatMessageProperty.Name, new WebBodyFormatMessageProperty(WebContentFormat.Raw));
return replyMessage;
}
}
And to send information to client we need data writer - RawDataWriter. Its implementation is simple:
class RawDataWriter : BodyWriter
{
byte[] data;
public RawDataWriter(byte[] data)
: base(true)
{
this.data = data;
}
protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
{
writer.WriteStartElement("Binary");
writer.WriteBase64(data, 0, data.Length);
writer.WriteEndElement();
}
}
Applying all code will result in returning date in more friendly format:
"DateTime":"2018-08-28T20:56:48.6411976+02:00"
To show it in practice I created example in the github branch DateTimeFormatter.
Please check also this answer as very likely you also will need it.
There is a limitation in JSON to convert DateTime, specially according to your case.
Please see http://msdn.microsoft.com/en-us/library/bb412170(v=vs.110).aspx
and read the section Dates/Times and JSON
To resolve this problem, I simply changed the type of serialization from JSON to XML for all the calls including DateTime.
After long time discussion ,I have find out the solution for it.
Please Use the following Code to Solve serialized date..
[IgnoreDataMember]
public DateTime? PerformanceDate { get; set; }
[DataMember(EmitDefaultValue = false, Name = "PerformanceDate")]
public string UpdateStartDateStr
{
get
{
if (this.PerformanceDate.HasValue)
return this.PerformanceDate.Value.ToUniversalTime().ToString("s", CultureInfo.InvariantCulture);
else
return null;
}
set
{
// should implement this...
}
}
Following this example.
GET response is:
{
"singer":"Metallica",
"title":"Enter Sandman"
}
If more objects were included output should be like this:
[{
"singer":"Metallica",
"title":"Enter Sandman"
}, {
"singer":"Elvis",
"title":"Rock"
}]
I want to get the 'classname' written too. Something like this:
{"Track":[ {
"singer":"Metallica",
"title":"Enter Sandman"
}, {
"singer":"Elvis",
"title":"Rock"
}]}
Any simple ways to achieve this?
Looking forward to get data directly into Datatables from a JAX-RS Resteasy (Jackson) Server. Also trying to avoid DTO.
class TrackList
{
private List<Track> Track = new ArrayList<Track>();
// setter, getter
}
GET method
public TrackList getTrackInJSON() {
EDIT
GET method
public String getTrackInJSON() {
// ... create list of objects
return convertToString(objects);
}
utility method
static <T> String convertToString(List<T> list) throws IOException
{
final String json = new ObjectMapper().writeValueAsString(list);
return new StringBuilder()
.append("{\"")
.append(list.get(0).getClass().getSimpleName())
.append("\":")
.append(json)
.append("}")
.toString();
}