jsonPath cast issue in Junit5 spring boot with MockMVC - java

I have the below test case with imports as
import org.springframework.http.MediaType;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.assertj.core.internal.bytebuddy.matcher.ElementMatchers.is;
import static org.mockito.Mockito.doReturn;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
#Test
#DisplayName("Should return product based on the specified Id")
void shouldReturnProductBasedOnTheSpecifiedId() throws Exception {
String Id = java.util.UUID.randomUUID().toString();
ProductViewModel productViewModel = new ProductViewModel(Id, "Product 1", 100, "Product 1 description", 0);
doReturn(productViewModel).when(productService).findById(Id);
mockMvc.perform(get(String.format("/api/v1/product/%s", Id)))
//Validate the response code and content type
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
//validate the headers
.andExpect(header().string(HttpHeaders.ETAG, String.format("\"%s\"", Id)))
.andExpect(header().string(HttpHeaders.LOCATION, String.format("/%s", Id)))
//Validate the return fields
.andExpect(jsonPath("$.id", is(Id)));
//.andExpect((ResultMatcher) jsonPath("$.name", is("Product 1")))
//.andExpect((ResultMatcher) jsonPath("$.price", is(100)))
//.andExpect((ResultMatcher) jsonPath("$.description", is("Product 1 description")))
//.andExpect((ResultMatcher) jsonPath("$.version", is(0)));
}
Getting an error as
If I cast the object I get an cast error message.

There are several ways to solve it:
Include individual Json properties comparison
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.hamcrest.core.Is.is;
mockMvc.perform(get(String.format("/api/v1/product/%s", Id))
.andExpect(content().contentType(APPLICATION_JSON_VALUE))
.andExpect(jsonPath("$.id", is(Id)));
The difference with your example is on "is import".
Deserialize Json response into a class and compare objects
This is a more generic and reusable approach.
Use a Json deserialize method to convert that one into an object of a well-known class instance.
Compare expected object with the received one in the test.
You can see a complete example of it in the following link, more specifically in the test:
static Stream<Arguments> findByIdWithOrderLines_validIdTestCases() {
PizzaDto pizzaDto = new PizzaDto((short)1, "Carbonara", 7.50);
OrderLineDto orderLineDto = new OrderLineDto(10, 1, pizzaDto, (short)2, 15D);
OrderDto dto = new OrderDto(1, "Order 1", new Date(), asList(orderLineDto));
return Stream.of(
//#formatter:off
// serviceResult, expectedResultHttpCode, expectedBodyResult
Arguments.of( empty(), NOT_FOUND, null ),
Arguments.of( of(dto), OK, dto )
); //#formatter:on
}
#ParameterizedTest
#SneakyThrows
#WithMockUser(authorities = {Constants.ROLE_ADMIN})
#MethodSource("findByIdWithOrderLines_validIdTestCases")
#DisplayName("findByIdWithOrderLines: when given Id verifies the validations then the suitable Http code is returned")
public void findByIdWithOrderLines_whenGivenIdVerifiesValidations_thenSuitableHttpCodeIsReturned(Optional<OrderDto> serviceResult,
HttpStatus expectedResultHttpCode, OrderDto expectedBodyResult) {
// Given
Integer validOrderId = 1;
// When
when(mockOrderService.findByIdWithOrderLines(validOrderId)).thenReturn(serviceResult);
ResultActions result = mockMvc.perform(get(RestRoutes.ORDER.ROOT + "/" + validOrderId + RestRoutes.ORDER.WITH_ORDERLINES));
// Then
result.andExpect(status().is(expectedResultHttpCode.value()));
assertEquals(expectedBodyResult, fromJson(result.andReturn().getResponse().getContentAsString(), OrderDto.class));
verify(mockOrderService, times(1)).findByIdWithOrderLines(validOrderId);
}

Related

Java MockMvc Test and Match Json String

i want to match output of get-request, and match with manual provided json string, basically copy one json string coming out of whole output and test. Tried lot of ways but its not happening, i can't delete all rows from database to match one string to one, so, i want to match only one json string -- Like this below
{
"instanceName": "",
"read": 1,
"swProduct": "Area 1",
"swProductModule": "Regular 1"
},
Here's, my test code ---
#SpringBootTest(classes = TestApplication.class)
#ActiveProfiles("dev")
#AutoConfigureMockMvc
public class SwStatusCheckTest {
#Autowired
private MockMvc mvc;
#IfProfileValue(name = "spring.profiles.active", values = { "dev" })
#Test
#DisplayName("GET Method /SwCheckStatus check success")
public void testStatusSuccess() throws Exception {
MvcResult result = mvc.perform(get("/status/SwCheckStatus"))
.andDo(print())
.andExpect(status().isOk())
.andReturn();
//.andExpect(content().json("{\"services\":[\"OutboundMessageService\"]}", true));
String actualJson = result.getResponse().getContentAsString();
String expectedJson = "[{\"instanceName\":\"Instance C\", \"read\" : 1, \"swProduct\" : \"Area 3\", \"swProductModule\" : \"Spring Boot 3\"}]";
assertThat(expectedJson).isIn(actualJson);
}
}
Output of whole Postman json result looks like this ---
[
{
"instanceName": "",
"read": 1,
"swProduct": "Area 1",
"swProductModule": "Regular 1"
},
{
"instanceName": "",
"read": 1,
"swProduct": "Area 1",
"swProductModule": "Regular 2"
},
{
"instanceName": "",
"read": 1,
"swProduct": "Area 1",
"swProductModule": "Spring Boot 1"
},
{
"instanceName": "",
"read": 1,
"swProduct": "Area 1",
"swProductModule": "Spring Boot 2"
},
Intellij says -- its expecting and String matches... but test failed -- check attachment ---
[enter image description here][1]
Any help of insights to perform the test is greatly appreciated.
[1]: https://i.stack.imgur.com/FE2dT.jpg
isIn is not for comparing strings, but arrays.
You can compare JSON as Map, with jackson-databind and Google Guava:
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Maps;
import java.util.List;
import java.util.Map;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.IfProfileValue;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
#SpringBootTest(classes = TestApplication.class)
#ActiveProfiles("dev")
#AutoConfigureMockMvc
public class SwStatusCheckTest {
#Autowired
private MockMvc mvc;
#IfProfileValue(name = "spring.profiles.active", values = { "dev" })
#Test
#DisplayName("GET Method /SwCheckStatus check success")
public void testStatusSuccess() throws Exception {
MvcResult result = mvc.perform(get("/status/SwCheckStatus"))
.andDo(print())
.andExpect(status().isOk())
.andReturn();
//.andExpect(content().json("{\"services\":[\"OutboundMessageService\"]}", true));
ObjectMapper mapper = new ObjectMapper();
List<? extends Map<String, Object>> actualJson = mapper.readValue(result.getResponse().getContentAsString(),
List.class);
Map<String, Object> expected = Map.of("instanceName", "", "read", 1, "swProduct", "Area 1", "swProductModule",
"Regular 1");
boolean contains = false;
for (Map<String, Object> a : actualJson) {
boolean res = Maps.difference(a, expected).areEqual();
if (res) {
contains = true;
break;
}
}
assertThat(contains).isTrue();
}
}
If I'm reading your question right as well as the image you posted, the problem is either the expected/actual Strings or the condition (assertion) defined in the test. The "actual" String value is an array of several elements, the "expected" is an array of only one element. Since the array of just one element is NOT contained in the array of several elements, it won't work.
If the code is changed such that the "expected" value you're looking for is written as JUST the object (without its enclosing array)
{ ... your object here ... }
Then the expected "isIn" actual assertion should work.

Map JSON string or JSON array to String in Java object [duplicate]

This question already has answers here:
Make Jackson interpret single JSON object as array with one element
(3 answers)
Deserializing json to pojo where json field has different data types
(2 answers)
Closed 1 year ago.
I have a JSON created by Elixir class which has a field which can be string or array:
field :logs, {:array, :string}
If anyone doesn't get this it will be like
{"logs" : "some log 1"}
or
{
"logs": ["some log 1", "some log 2"]
}
I have a Java field mapped for this:
#JsonProperty("logs")
private String logs;
This mapping works only when the logs comes as a String, but fails if the logs comes as array with error saying it will not be able to convert START_ARRAY to string.
How to serialize the field if it comes as array and store it as a comma separated string?
I see in tags that you use Jackson for parsing. This means you need to write and register with Jackson a custom deserializer for your logs field.
An example of such solution:
package tmp;
import java.io.IOException;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.exc.MismatchedInputException;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ValueNode;
public class JacksonDemo {
public static class LogHolder {
#JsonProperty("logs")
#JsonDeserialize(using = ArrayOrStringJsonDeserializer.class)
private String logs;
#Override
public String toString() {
return "LogHolder(logs=" + logs + ")";
}
}
public static class ArrayOrStringJsonDeserializer extends JsonDeserializer<String> {
#Override
public String deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException, JsonProcessingException {
JsonNode node = (JsonNode) jsonParser.readValueAsTree();
if (node.isValueNode()) {
ValueNode valueNode = (ValueNode) node;
if (valueNode.isTextual()) {
return valueNode.textValue();
}
} else if (node.isArray()) {
ArrayNode arrayNode = (ArrayNode) node;
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(arrayNode.iterator(), Spliterator.ORDERED), false)
.map(JsonNode::textValue)
.collect(Collectors.joining(", "));
}
throw MismatchedInputException.from(jsonParser, String.class,
"Expected node to be of type String or array, but got " + node.getNodeType().toString());
}
}
public static void main(String args[]) throws Exception {
String[] docs = { "{\"logs\" : \"some log 1\"}", "{\"logs\": [\"some log 1\", \"some log 2\"]}" };
ObjectMapper om = new ObjectMapper();
for (String doc : docs) {
System.out.println(om.readValue(doc, LogHolder.class));
}
}
}
Result of executing this code:
LogHolder(logs=some log 1)
LogHolder(logs=some log 1, some log 2)

AssertionError: No value at JSON path, but value exists in valid JSON response

I saw many of solutions provided here, on the forum, and also a guide for the problem (provided here), but none of them helped me...
I tried to keep my code cleare as much as I can, so I decided to create many of test classes including test items, for example:
#Getter
#Setter
public class SalesTaskTestItem {
public static SalesTask buildTestItem(){
List<ContactPerson> contactPersonTestItemList = new ArrayList<>();
contactPersonTestItemList.add(ContactPersonTestItem.buildTestItem());
List<SalesMan> supportingSalesTeamList = new ArrayList<>();
List<Product> discussedProductsTestList = new ArrayList<>();
discussedProductsTestList.add(ProductTestItem.buildTestItem());
List<SalesTaskProgress> progressTestList = new ArrayList<>();
progressTestList.add(SalesTaskProgressTestItem.buildTestItemNo1());
progressTestList.add(SalesTaskProgressTestItem.buildTestItemNo2());
progressTestList.add(SalesTaskProgressTestItem.buildTestItemNo3());
List<Offer> alreadySentOffersTestList = new ArrayList<>();
alreadySentOffersTestList.add(OfferTestItem.buildTestItem());
List<AssignedTaskDocument> assignedTaskDocumentsTestList = new ArrayList<>();
assignedTaskDocumentsTestList.add(AssignedTaskDocumentTestItem.buildTestItem());
List<InternalProcedureDocument> internalProceduresDocumentsTestList = new ArrayList<>();
internalProceduresDocumentsTestList.add(InternalProcedureDocumentTestItem.buildTestItem());
SalesTask testItem = new SalesTask();
testItem.setId(1L);
testItem.setVersion(1);
testItem.setTaskEstablishedDate(DateFormatter.fromStringToDate("10-12-2020T09:12:45"));
testItem.setLastProgressDate(DateFormatter.fromStringToDate("10-12-2020T09:30:56"));
testItem.setCompany(CompanyTestItem.buildTestItem());
testItem.setContactPersonsList(contactPersonTestItemList);
testItem.setMainSalesMan(SalesManTestItem.buildTestItemNo1());
testItem.setSupportingSalesTeam(supportingSalesTeamList);
testItem.setDiscussedProducts(discussedProductsTestList);
testItem.setProgressList(progressTestList);
testItem.setCurrentTaskValue(BigDecimal.valueOf(250000));
testItem.setChanceOfPositiveFinishingTask(0.45);
testItem.setEstimatedDateOfFinishingTask(DateFormatter.fromStringToDate("30-12-2020T13:00:00"));
testItem.setAlreadySentOffersList(alreadySentOffersTestList);
testItem.setAssignedTaskDocumentsList(assignedTaskDocumentsTestList);
testItem.setInternalProceduresDocumentsList(internalProceduresDocumentsTestList);
return testItem;
}
}
My test case is:
package com.jmdev.storycrm.controllers;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.jmdev.storycrm.domain.salesTask.SalesTask;
import com.jmdev.storycrm.services.SalesTaskService;
import com.jmdev.storycrm.testDomainItems.salesTask.SalesTaskTestItem;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
#ExtendWith(SpringExtension.class)
#WebMvcTest
public class SalesTaskControllerTest {
#Autowired
MockMvc mockMvc;
#MockBean
private SalesTaskService salesTaskService;
#Test
public void createNewSalesTask(){
SalesTask newSalesTask = new SalesTask();
newSalesTask = SalesTaskTestItem.buildTestItem();
when(salesTaskService.save(any(SalesTask.class))).thenReturn(newSalesTask);
ObjectMapper objectMapper = new ObjectMapper().registerModule(new JavaTimeModule());
try {
String newSalesTaskJSON = objectMapper.writeValueAsString(newSalesTask);
ResultActions resultActions = mockMvc.perform(
post("/salesTask")
.contentType(MediaType.APPLICATION_JSON)
.content(newSalesTaskJSON)
);
resultActions.andExpect(status().isCreated())
.andDo(print())
.andExpect(content().contentType("application/json"))
.andExpect(jsonPath("$.id").value(1L))
.andExpect(jsonPath("$.version").value(1))
.andExpect(jsonPath("$.taskEstablishedDate").value(SalesTaskTestItem.buildTestItem().getTaskEstablishedDate().toString()))
.andExpect(jsonPath("$.lastProgressDate").value(SalesTaskTestItem.buildTestItem().getLastProgressDate().toString()))
.andExpect(jsonPath("$.company").value(SalesTaskTestItem.buildTestItem().getCompany()))
.andExpect(jsonPath("$.contactPersonsList").value(SalesTaskTestItem.buildTestItem().getContactPersonsList()))
.andExpect(jsonPath("$.mainSalesMan").value(SalesTaskTestItem.buildTestItem().getMainSalesMan()))
.andExpect(jsonPath("$.supportingSalesTeam").value(SalesTaskTestItem.buildTestItem().getSupportingSalesTeam()))
.andExpect(jsonPath("$.discussedProducts").value(SalesTaskTestItem.buildTestItem().getDiscussedProducts()))
.andExpect(jsonPath("$.progressList").value(SalesTaskTestItem.buildTestItem().getProgressList()))
.andExpect(jsonPath("$.currentTaskValue").value(SalesTaskTestItem.buildTestItem().getCurrentTaskValue()))
.andExpect(jsonPath("$.chanceOfPositiveFinishingTask").value(SalesTaskTestItem.buildTestItem().getChanceOfPositiveFinishingTask()))
.andExpect(jsonPath("$.estimatedDateOfFinishingTask").value(SalesTaskTestItem.buildTestItem().getEstimatedDateOfFinishingTask()))
.andExpect(jsonPath("$.alreadySentOffersList").value(SalesTaskTestItem.buildTestItem().getAlreadySentOffersList()))
.andExpect(jsonPath("$.assignedTaskDocumentsList").value(SalesTaskTestItem.buildTestItem().getAssignedTaskDocumentsList()))
.andExpect(jsonPath("$.internalProceduresDocumentsList").value(SalesTaskTestItem.buildTestItem().getInternalProceduresDocumentsList()));
} catch (Exception e) {
e.printStackTrace();
}
}
}
I see in stack trace my MockHttpServletResponse, where I can find "taskEstablishedDate":"2020-12-10T09:12:45.000+00:00". Also, If I copy whole body of this response and past it into https://jsonformatter.curiousconcept.com/, validation is passed with no problems.
MockHttpServletResponse:
Status = 201
Error message = null
Headers = [Content-Type:"application/json"]
Content type = application/json
Body = {"id":1,"version":1,"taskEstablishedDate":"2020-12-10T09:12:45.000+00:00","lastProgressDate":"2020-12-10T09:30:56.000+00:00", (...) }
Forwarded URL = null
Redirected URL = null
Cookies = []
But I get java.lang.AssertionError: No value at JSON path "$.taskEstablishedDate".
I really don't know what is wrong. Could anybody help?
EDIT: I implemented SSK suggestions and dates and time are passing now. For test next of JSON values, provided as my app's objects, I implemented GSON into override toString() method:
package com.jmdev.storycrm.domain.company;
import com.jmdev.storycrm.utils.JSONFormatter;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.Entity;
import javax.persistence.Id;
#Entity
#Getter
#Setter
public class Company {
#Id
private Long id;
private String name;
private Long nipNumber;
private Address address;
#Override
public String toString() {
return JSONFormatter.useGSON(this);
}
}
Used toString() formatter:
package com.jmdev.storycrm.utils;
public class JSONFormatter {
public static String useGSON(Object object){
return new com.google.gson.Gson().toJson(object);
}
}
But I'm getting java.lang.AssertionError for both values which are completely the same...
java.lang.AssertionError: JSON path "$.company" (...)
Expected :{"id":1,"name":"TestCompanyName","nipNumber":345353534354335,"address":{"id":1,"voivodeship":"Region name","postalCode":"99-000","city":"My City name","street":"Main Street name","fullBiuldingNumber":"100","flatNumber":1}}
Actual :{"id":1,"name":"TestCompanyName","nipNumber":345353534354335,"address":{"id":1,"voivodeship":"Region name","postalCode":"99-000","city":"My City name","street":"Main Street name","fullBiuldingNumber":"100","flatNumber":1}}
You need to register your JavaTimeModule with ObjectMapper.
You can register JavaTimeModule as shown below.
ObjectMapper objectMapper = new ObjectMapper().registerModule(new JavaTimeModule());
Then use the same objectMapper to create the json string
String newSalesTaskJSON = objectMapper.writeValueAsString(newSalesTask);
JavaTimeModule is from com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
Also, you need to add .toString() to your date values in .andExpect() as shown below
.andExpect(jsonPath("$.taskEstablishedDate").value(SalesTaskTestItem.buildTestItem().getTaskEstablishedDate().toString()))
.andExpect(jsonPath("$.lastProgressDate").value(SalesTaskTestItem.buildTestItem().getLastProgressDate().toString()))
.andExpect(jsonPath("$.estimatedDateOfFinishingTask").value(SalesTaskTestItem.buildTestItem().getEstimatedDateOfFinishingTask().toString()))

Pretty JSON import when filtering but not when returning whole lot + ugly brackets in ID

I'm trying to achieve these 2 things:
Method getData() to return nicely formed JSON like getById() & getBetween return already.
How to get rid of those ugly brackets (original csv file I'm reading from comes with them) around ID so I could return UUID for Id, rather than a String as I currently do.
Here's the code:
import java.io.InputStreamReader;
import java.time.LocalDate;
import java.util.List;
import java.util.stream.Collectors;
import com.opencsv.bean.CsvToBean;
import com.opencsv.bean.CsvToBeanBuilder;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import com.foobar.hotovo.Domain.PricePaid;
import com.foobar.hotovo.Exceptions.PriceNotFoundException;
#Service
public class MainService {
private static final String URL = "http://prod.publicdata.landregistry.gov.uk.s3-website-eu-west-1.amazonaws.com/pp-monthly-update-new-version.csv";
private List<PricePaid> getData() {
RestTemplate restTemplate = new RestTemplate();
return restTemplate.execute(URL, HttpMethod.GET, null, clientHttpResponse -> {
InputStreamReader reader = new InputStreamReader(clientHttpResponse.getBody());
CsvToBean<PricePaid> csvToBean = new CsvToBeanBuilder<PricePaid>(reader)
.withType(PricePaid.class)
.withSeparator(',')
.withIgnoreLeadingWhiteSpace(true)
.build();
return csvToBean.stream().collect(Collectors.toList());
});
}
private boolean isBetween(PricePaid pricePaid, LocalDate from, LocalDate to) {
LocalDate date = pricePaid.getDateOfTransfer();
return date.isAfter(from) && date.isBefore(to);
}
public List<PricePaid> getAll() {
return getData();
}
public PricePaid getById(String id) throws PriceNotFoundException {
return getData().stream()
.filter(pricePaid -> pricePaid.getId().equals(id))
.findFirst()
.orElseThrow(() -> new PriceNotFoundException("Item not found!"));
}
public List<PricePaid> getBetween(LocalDate from, LocalDate to) {
return getData().stream()
.filter(pricePaid -> isBetween(pricePaid, from, to))
.collect(Collectors.toList());
}
}
Here's an example of the output from getData():
[{"id":"{9DBAD221-7F8E-6EB3-E053-6B04A8C0F257}","price":96000,"dateOfTransfer":"2002-05-31","postCode":"SA62 3JW","propertyType":"F","oldNew":"Y","duration":"L","paon":"THE COACH HOUSE, 29","saon":"FLAT 5","street":"ENFIELD ROAD","locality":"BROAD HAVEN","city":"HAVERFORDWEST","district":"PEMBROKESHIRE","county":"PEMBROKESHIRE","ppd":"A","recordStatus":"A"},{"id":"{9DBAD221-8683-6EB3-E053-6B04A8C0F257}","price":59500,"dateOfTransfer":"2002-12-05","postCode":"EX31 2BS","propertyType":"F","oldNew":"N","duration":"L","paon":"16","saon":"FLAT 3","street":"STICKLEPATH HILL","locality":"STICKLEPATH","city":"BARNSTAPLE","district":"NORTH DEVON","county":"DEVON","ppd":"A","recordStatus":"A"},{"id":"{9DBAD221-B238-6EB3-E053-6B04A8C0F257}","price":240000,"dateOfTransfer":"2002-02-15","postCode":"GL7 5BL","propertyType":"S","oldNew":"N","duration":"F","paon":"76","saon":"","street":"CHURCH ROAD","locality":"QUENINGTON","city":"CIRENCESTER","district":"COTSWOLD","county":"GLOUCESTERSHIRE","ppd":"A","recordStatus":"A"},{"id":"{9DBAD221-CBFB-6EB3-E053-6B04A8C0F257}","price":292500,"dateOfTransfer":"2002-09-27","postCode":"WR11 8QH","propertyType":"T","oldNew":"N","duration":"F","paon":"BIG BARN","saon":"","street":"","locality":"ULLINGTON","city":"EVESHAM","district":"WYCHAVON","county":"WORCESTERSHIRE","ppd":"A","recordStatus":"A"},{"id":"{9DBAD221-E1D3-6EB3-E053-6B04A8C0F257}","price":145000,"dateOfTransfer":"2002-01-25","postCode":"PE12 8SN","propertyType":"D","oldNew":"N","duration":"F","paon":"SILVERCROFT","saon":"","street":"RAVENS DROVE","locality":"HOLBEACH FEN","city":"SPALDING","district":"SOUTH HOLLAND","county":"LINCOLNSHIRE","ppd":"A","recordStatus":"A"},{"id":"{9DBAD222-5429-6EB3-E053-6B04A8C0F257}","price":79950,"dateOfTransfer":"2002-07-17","postCode":"NE29 6XJ","propertyType":"S","oldNew":"N","duration":"F","paon":"54","saon":"","street":"BLUCHER ROAD","locality":"","city":"NORTH SHIELDS","district":"NORTH TYNESIDE","county":"TYNE AND WEAR","ppd":"A","recordStatus":"A"},{"id":"{9DBAD222-5451-6EB3-E053-6B04A8C0F257}","price":65000,"dateOfTransfer":"2002-11-13","postCode":"NE6 5XY","propertyType":"F","oldNew":"N","duration":"L","paon":"71","saon":"","street":"KING JOHN TERRACE","locality":"","city":"NEWCASTLE UPON TYNE","district":"NEWCASTLE UPON TYNE","county":"TYNE AND WEAR","ppd":"A","recordStatus":"A"},{"id":"{9DBAD222-5F1E-6EB3-E053-6B04A8C0F257}","price":17000,"dateOfTransfer":"2002-07-01","postCode":"SY23 5NJ","propertyType":"D","oldNew":"N","duration":"F","paon":"TYNEWYDD","saon":"","street":"","locality":"BETHANIA","city":"LLANON","district":"CEREDIGION","county":"CEREDIGION","ppd":"A","recordStatus":"A"},{"id":"{9DBAD222-7BB6-6EB3-E053-6B04A8C0F257}","price":33000,"dateOfTransfer":"2002-01-31","postCode":"HD3 4QJ","propertyType":"T","oldNew":"N","duration":"L","paon":"26 - 28","saon":"","street":"SCAR LANE","locality":"","city":"HUDDERSFIELD","district":"KIRKLEES","county":"WEST YORKSHIRE","ppd":"A","recordStatus":"A"},{"id":"{9DBAD221-5212-6EB3-E053-6B04A8C0F257}","price":127000,"dateOfTransfer":"2002-11-28","postCode":"BS6 5QZ","propertyType":"T","oldNew":"N","duration":"F","paon":"200","saon":"","street":"CHELTENHAM ROAD","locality":"","city":"BRISTOL","district":"CITY OF BRISTOL","county":"CITY OF BRISTOL","ppd":"A","recordStatus":"A"},{"id":"{9DBAD221-65C4-6EB3-E053-6B04A8C0F257}","price":105000,"dateOfTransfer":"2002-11-01","postCode":"PE19 7BH","propertyType":"D","oldNew":"Y","duration":"F","paon":"287A","saon":"","street":"GREAT NORTH ROAD","locality":"EATON FORD","city":"ST NEOTS","district":"HUNTINGDONSHIRE","county":"CAMBRIDGESHIRE","ppd":"A","recordStatus":"A"},{"id":"{9DBAD221-79A7-6EB3-E053-6B04A8C0F257}","price":365000,"dateOfTransfer":"2002-05-31","postCode":"LA23 2HB","propertyType":"D","oldNew":"N","duration":"F","paon":"30","saon":"","street":"CRAIG WALK","locality":"","city":"WINDERMERE","district":"SOUTH LAKELAND","county":"CUMBRIA","ppd":"A","recordStatus":"A"},{"id":"{9DBAD221-8270-6EB3-E053-6B04A8C0F257}","price":58000,"dateOfTransfer":"2002-05-13","postCode":"SA62 3JW","propertyType":"F","oldNew":"Y","duration":"L","paon":"THE COACH HOUSE, 29","saon":"FLAT 3","street":"ENFIELD ROAD","locality":"BROAD HAVEN","city":"HAVERFORDWEST","district":"PEMBROKESHIRE","county":"PEMBROKESHIRE","ppd":"A","recordStatus":"A"},{"id":"{9DBAD221-9E52-6EB3-E053-6B04A8C0F257}","price":173000,"dateOfTransfer":"2002-09-16","postCode":"BN27 4BP","propertyType":"D","oldNew":"N","duration":"F","paon":"SUNNYSIDE","saon":"","street":"HACKHURST LANE","locality":"LOWER DICKER","city":"HAILSHAM","district":"WEALDEN","county":"EAST SUSSEX","ppd":"A","recordStatus":"A"},{"id":"{9DBAD221-A6F1-6EB3-E053-6B04A8C0F257}","price":110200,"dateOfTransfer":"2002-02-28","postCode":"RM16 6LW","propertyType":"F","oldNew":"Y","duration":"L","paon":"5","saon":"","street":"SAN MARCOS DRIVE","locality":"CHAFFORD HUNDRED","city":"GRAYS","district":"THURROCK","county":"THURROCK","ppd":"A","recordStatus":"A"},{"id":"{9DBAD221-B31F-6EB3-E053-6B04A8C0F257}","price":475000,"dateOfTransfer":"2002-03-28","postCode":"GL50 4LB","propertyType":"D","oldNew":"Y","duration":"F","paon":"PARK WAY","saon":"","street":"WEST DRIVE","locality":"","city":"CHELTENHAM","district":"CHELTENHAM","county":"GLOUCESTERS
You could use a library like Gson or Jackson to pretty print the json string:
Gson method
Jackson method

Updating specific part of value in hash map dynamically

I have a spring boot app with a HTTP post request handler. It accepts a payload that I parse and outputs a JSON. I have handled it that it needs to accept a payload of certain parameters(18).
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.google.gson.Gson;
#Validated
#RestController
public class MockController {
#Autowired
MockConfig mockconfig;
private static final Logger LOGGER = LoggerFactory.getLogger(MockController.class);
#RequestMapping(value = "/", method = RequestMethod.GET)
public String index() {
return "hello!";
}
String[] parse;
#PostMapping(value = "/")
public String payloader(#RequestBody String params ) throws IOException{
LOGGER.debug("code is hitting");
parse = params.split("\\|");
String key;
String value;
String dyn;
Map<String, String> predictionFeatureMap = mockconfig.getPredictionFeatureMap();
if(parse.length!=18) {
key = "Not_enough_parameters";
value = predictionFeatureMap.get(key);
Map<?, ?> resultJsonObj = new Gson().fromJson(value, Map.class);
}
else {
key = params;
value = predictionFeatureMap.get(key);
}
return value;
}
}
My config class is where I get the input and output from a file and put them into a hashmap.
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import org.springframework.context.annotation.Configuration;
#Configuration
public class MockConfig {
private Map<String, String> predictionFeatureMap = new HashMap<String, String>();
public Map<String,String> getPredictionFeatureMap() throws IOException {
return predictionFeatureMap;
}
public MockConfig() throws IOException {
init();
}
private Map<String, String> init() throws IOException {
Scanner sc = new Scanner (new File("src/test/resources/Payload1.txt"));
int counter = 1;
String key = "";
while (sc.hasNextLine()) {
if(counter % 2 != 0) {
key = sc.nextLine();
}else {
predictionFeatureMap.put(key, sc.nextLine());
}
counter++;
}
sc.close();
return predictionFeatureMap;
}
}
This is the key and value in the file that I'm trying to work with specifically.
Not_enough_parameters
{"status": false, "errorMessage": "Payload has incorrect amount of parts: expecting: 18, actual:8", "version": "0.97", "coreName": "Patient_Responsibility"}
(The JSON string is the response to too much or too little parameters... the paramter length should be 18.)
Example input:
ncs|56-2629193|1972-03-28|20190218|77067|6208|3209440|self|-123|-123|-123|0.0|0.0|0.0|0.0|0.0|0.0|0.0
This input would pass because it has 18 parameters...
What I want to do is if a user curls for example 5 parameters
ncs|56-2629193|1972-03-28|20190218|77067
I want the value(JSON error message) to have the 'actual' field updated like:
{"status": false, "errorMessage": "Payload has incorrect amount of parts: expecting: 18, actual:5", "version": "0.97", "coreName": "Patient_Responsibility"}
without hardcoding it into the txt file or hashmap...
I have tried getting the index of the string and replacing the '8' character with parse.length() and casting it as a char but it just gives me this output:
{"status": false, "errorMessage": "Payload has incorrect amount of parts: expecting:1 actual:", "version": "0.97", "coreName": "Nulogix_Patient_Responsibility"}
How do I parse or index the JSON to update this value? Or is there a hashmap method to deal with this?
When working with a framework, you usually handle errors using the frameworks way of handling errors.
To handle errors in spring boot you typically use a controller advice that will assist in handling errors. This is created by annotating a class with #ControllerAdvice.
There you can catch thrown exceptions and build responses that will be returned to the calling client.
#PostMapping(value = "/")
public String payloader(#RequestBody String params ) throws IOException{
LOGGER.debug("code is hitting");
parse = params.split("\\|");
String key;
String value;
String dyn;
Map<String, String> predictionFeatureMap = mockconfig.getPredictionFeatureMap();
if(parse.length!=18) {
// Here you throw a custom runtime exception and pass what
// parameters you want that will help you build your error
// message you are passing to your client.
final String errorMessage = String.format(
"Payload has incorrect amount of parts: expecting:%d actual:%d",
predictionFeatureMap.size(),
parse.length);
throw new MyException(errorMessage);
}
else {
key = params;
value = predictionFeatureMap.get(key);
}
return value;
}
Then in a controller advice class
#ControllerAdvice
public class Foo extends ResponseEntityExceptionHandler {
#ExceptionHandler(MyException.class)
public ResponseEntity<MyCustomErrorBody> handleControllerException(HttpServletRequest request, MyException ex) {
// Build your error response here and return it.
// Create a class that will represent the json error body
// and pass it as body and jackson will deserialize it for
// you into json automatically.
final MyCustomErrorBody body = new MyCustomErrorBody(false, ex.getMessage(), "0.97", "Patient_Responsibility")
return ResponseEntity.unprocessableEntity().body(myCustomErrorBody).build();
}
}
public class MyCustomErrorBody {
private boolean status;
private String errorMessage;
private String version;
private String coreName;
// constructor and getters setters ommitted
}
Links about spring boot error handling:
Official spring boot documentation
Baeldung exception-handling-for-rest-with-spring

Categories