I have a List type field populated using solrj where it marshals data directly to bean using getBean() method. The solr field is marked as multivalued but it really is single valued. In the rest response I want to transmit it as a single string. Here is the code
#XmlRootElement
#JsonSerialize(include = Inclusion.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
public class Record {
#JsonIgnore
#Field //solrj field populated based on schema type
private List<String> titleList;
public String getTitle() {
if(titleList!= null && titleList.size() > 0) {
return titleList.get(0);
}
return "";
}
}
When I get the response object from non jersey rest clients I see the 'title' field populated correctly as String but with jersey REST client I get it as empty String. How can it be correctly deserialized as derived value for all REST clients?
I am getting value from java client as
Record response = target.queryParams(queryParams).request().buildGet().invoke(Record.class);
Chrome Rest client output
{
"title": "new trend",
jersey client output
{
"title" : "",
I used #JsonIgnore on both getter and setter methods instead of field. That worked for both deserialization and serialization
#Field("title")
private List<String> titleList;
#JsonIgnore
public List<String> getTitleList() {
return titleList;
}
#JsonIgnore
public void setTitleList(List<String> titleList) {
this.titleList= titleList;
}
public String getTitle() {
if(titleList!= null && titleList.size() > 0) {
return titleList.get(0);
}
return null;
}
Related
I am trying to implement custom validation in spring boot but there is some issue with the code even if I am passing wrong phone number the phone number should be 10 digit but I am passing a string and still I get status as OK can anyone tell me what is wrong with my code
#Data
public class StudentData {
#Phone
String phone;
}
#RestController
public class HomeController {
#PostMapping("/showData")
public String display(#Valid #RequestBody StudentData studentData){
return "OK";
}
}
#Documented
#Constraint(validatedBy = PhoneValidation.class)
#Target( { ElementType.METHOD, ElementType.FIELD })
#Retention(RetentionPolicy.RUNTIME)
public #interface Phone {
String message() default "{Wrong phone}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class PhoneValidation implements ConstraintValidator<Phone, String> {
#Override
public void initialize(Phone phone) { }
#Override
public boolean isValid(String phoneField, ConstraintValidatorContext constraintValidatorContext) {
if(phoneField == null) {
return false;
}
if(phoneField.matches("^[0-9]*$"))
return true;
return false;
}
}
Payload I am passing
http://localhost:8080/showData
{
"phone":"abc"
}
Try adding #Validated to your controller.
If that does not work, I would also confirm that validation code is being hit. Running your API in debug mode with a breakpoint in the isValid method can confirm if this is a problem in the validation logic, the Bean validator injection or your test setup (is it a test scenario?).
First, you don't need to use #Validated over #RestController, servlet container will validate it for you on data input annotated with #Valid
Second, your regex validates all strings consisting of digits, in spite of their length, so "1" or "22222222222222222" is a valid Phone
And last but not least - your code is working as desired :)
I have table in AWS mobile hub and I am using the following model for it
public class UserstopcoreDO {
private String _userId;
private String _usertoplevel;
private String _usertopscore;
private String _username;
#DynamoDBHashKey(attributeName = "userId")
#DynamoDBAttribute(attributeName = "userId")
public String getUserId() {
return _userId;
}
public void setUserId(final String _userId) {
this._userId = _userId;
}
#DynamoDBAttribute(attributeName = "usertoplevel")
public String getUsertoplevel() {
return _usertoplevel;
}
#DynamoDBAttribute(attributeName = "username")
public String getUsername() {
return _username;
}
public void setUsername(final String _username) {
this._username = _username;
}
public void setUsertoplevel(final String _usertoplevel) {
this._usertoplevel = _usertoplevel;
}
#DynamoDBIndexHashKey(attributeName = "usertopscore", globalSecondaryIndexName = "usertopscore")
public String getUsertopscore() {
return _usertopscore;
}
public void setUsertopscore(final String _usertopscore) {
this._usertopscore = _usertopscore;
}
}
In the table, I have 1500+ records and now I want to fetch Top 10 records from it so for that I write the below query
final DynamoDBQueryExpression<UserstopcoreDO> queryExpression = new DynamoDBQueryExpression<>();
queryExpression.withLimit(10);
queryExpression.setScanIndexForward(false);
final PaginatedQueryList<UserstopcoreDO> results = mapper.query(UserstopcoreDO.class, queryExpression);
Iterator<UserstopcoreDO> resultsIterator = results.iterator();
if (resultsIterator.hasNext()) {
final UserstopcoreDO item = resultsIterator.next();
try {
Log.d("Item :",item.getUsertopscore());
} catch (final AmazonClientException ex) {
Log.e(LOG_TAG, "Failed deleting item : " + ex.getMessage(), ex);
}
}
But when I run the code it gives me an error
Caused by: java.lang.IllegalArgumentException: Illegal query expression: No hash key condition is found in the query
but in my condition, I did not need any condition because I want to fetch top 10 records instead of one specific record. So how to handle that condition ?
If you want to "query" DynamoDB without specifying all HashKeys, use a Scan instead, i.e. DynamoDBScanExpression. You probably also want to change your "usertopscore" to be a RangeKey instead of a HashKey.
From https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/dynamodbv2/datamodeling/DynamoDBQueryExpression.html every DynamoDBQueryExpression requires all the Hash Keys be set.
Also see boto dynamodb2: Can I query a table using range key only?
Please set the hash key in the query expression. Below is the example of query expression for main table and GSI (need to set the index name).
Querying the main table:-
Set the hash key value of the table.
UserstopcoreDO hashKeyObject = new UserstopcoreDO();
hashKeyObject.setUserId("1");
DynamoDBQueryExpression<UserstopcoreDO> queryExpressionForMainTable = new DynamoDBQueryExpression<UserstopcoreDO>()
.withHashKeyValues(hashKeyObject);
Querying the Index:-
Set the index name and hash key value of the index.
UserstopcoreDO hashIndexKeyObject = new UserstopcoreDO();
hashIndexKeyObject.setUsertoplevel("100");
DynamoDBQueryExpression<UserstopcoreDO> queryExpressionForGsi = new DynamoDBQueryExpression<UserstopcoreDO>()
.withHashKeyValues(hashIndexKeyObject).withIndexName("usertopscore");
GSI attributes in mapper:-
#DynamoDBIndexHashKey(attributeName = "usertoplevel", globalSecondaryIndexName = "usertopscore")
public String getUsertoplevel() {
return _usertoplevel;
}
#DynamoDBIndexRangeKey(attributeName = "usertopscore", globalSecondaryIndexName = "usertopscore")
public String getUsertopscore() {
return _usertopscore;
}
In my class, I have a property for a file attachment like so...
public class Certificate {
[Required]
// TODO: Wow looks like there's a problem with using regex in MVC 4, this does not work!
[RegularExpression(#"^.*\.(xlsx|xls|XLSX|XLS)$", ErrorMessage = "Only Excel files (*.xls, *.xlsx) files are accepted")]
public string AttachmentTrace { get; set; }
}
I don't see anything wrong with my regex, but I always get ModelState.IsValid false. This seems pretty trivial and simple regex, am I missing something? Do I need to write my own custom validation?
I'm populating AttachmentTrace via a regular input of type file:
<div class="editor-label">
#Html.LabelFor(model => model.AttachmentTrace)
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.AttachmentTrace, new { type = "file" })
#Html.ValidationMessageFor(model => model.AttachmentTrace)
</div>
The action method is just a regular action:
public ActionResult Create(Certificate certificate, HttpPostedFileBase attachmentTrace, HttpPostedFileBase attachmentEmail)
{
if (ModelState.IsValid)
{
// code ...
}
return View(certificate);
}
Ok, here's the solution I found. I'm sure there are other solutions out there. First a little background, because my application uses EF code-first migration, specifying a HttpPostedFileBase property type in my model, produces this error when adding migration:
One or more validation errors were detected during model generation:
System.Data.Entity.Edm.EdmEntityType: : EntityType
'HttpPostedFileBase' has no key defined. Define the key for this
EntityType. \tSystem.Data.Entity.Edm.EdmEntitySet: EntityType:
EntitySet 'HttpPostedFileBases' is based on type 'HttpPostedFileBase'
that has no keys defined.
So I really had to stick with using a string type for the AttachmentTrace property.
The solution is to employ a ViewModel class like this:
public class CertificateViewModel {
// .. other properties
[Required]
[FileTypes("xls,xlsx")]
public HttpPostedFileBase AttachmentTrace { get; set; }
}
Then create a FileTypesAttribute like so, I borrowed this code from this excellent post.
public class FileTypesAttribute : ValidationAttribute {
private readonly List<string> _types;
public FileTypesAttribute(string types) {
_types = types.Split(',').ToList();
}
public override bool IsValid(object value) {
if (value == null) return true;
var postedFile = value as HttpPostedFileBase;
var fileExt = System.IO.Path.GetExtension(postedFile.FileName).Substring(1);
return _types.Contains(fileExt, StringComparer.OrdinalIgnoreCase);
}
public override string FormatErrorMessage(string name) {
return string.Format("Invalid file type. Only {0} are supported.", String.Join(", ", _types));
}
}
In the controller Action, I needed to make a change to use the ViewModel instead, then map it back to my Entity using AutoMapper (which is excellent by the way):
public ActionResult Create(CertificateViewModel certificate, HttpPostedFileBase attachmentTrace, HttpPostedFileBase attachmentEmail) {
if (ModelState.IsValid) {
// Let's use AutoMapper to map the ViewModel back to our Certificate Entity
// We also need to create a converter for type HttpPostedFileBase -> string
Mapper.CreateMap<HttpPostedFileBase, string>().ConvertUsing(new HttpPostedFileBaseTypeConverter());
Mapper.CreateMap<CreateCertificateViewModel, Certificate>();
Certificate myCert = Mapper.Map<CreateCertificateViewModel, Certificate>(certificate);
// other code ...
}
return View(myCert);
}
For the AutoMapper, I created my own TypeConverter for the HttpPostedFileBase as follows:
public class HttpPostedFileBaseTypeConverter : ITypeConverter<HttpPostedFileBase, string> {
public string Convert(ResolutionContext context) {
var fileBase = context.SourceValue as HttpPostedFileBase;
if (fileBase != null) {
return fileBase.FileName;
}
return null;
}
}
That's it. Hope this helps out others who may have this same issue.
I am currently experimenting with validation attributes,
and now I am trying to validate my ViewModel which contains an EmailAddress with a custom validation attribute.
public class UserLoginModel
{
[Required]
[EmailAddress]
public string email { get; set; }
[Required]
public string password { get; set; }
public bool rememberMe { get; set; }
}
I have made a unit-test where I give a false email address and try to validate my viewmodel.
[TestMethod]
public void TestingInvalidEmailAddress()
{
UserLoginModel model = new UserLoginModel();
model = GetAValidLoginModel(); //Get a default-model where all parameters are correct
model.email = "thisisnotavalidemail.com";
ValidationContext context = new ValidationContext(model, null, null);
var results = new List<ValidationResult>();
bool validModel= Validator.TryValidateObject(model, context, results);
//This is always true
Assert.IsFalse(validModel);
}
The result of this test is always False.
So I checked my attribute, because I thought I might have made a mistake:
[TestMethod]
public void Email()
{
string email;
var attr = new EmailAddressAttribute();
email = "myemail#domain.com";
Assert.IsTrue(attr.IsValid(email));
email = "thisisnotavalidemail.com";
Assert.IsFalse(attr.IsValid(email)); //If this fails, the test is successfull
}
And that did pass the test, using the exact same email address.
And when I test it in my browser, it also validates correctly.
So why does it not tell me that my email address is invalid in the first test-method?
I found my solution in over here.
Apparently I am just missing an extra parameter.
bool validModel= Validator.TryValidateObject(model, context, results, **true**);
I was wondering if anyone had any pointers for parsing json data consumed from a URL in Asp.Net. I've found plenty of docs about Model Binding json datatypes but this is coming from a URL and I cant seem to find an example for that. The closest thing I've found is datacontractjsonserializer but again, I cant seem to find an example of that in context with a URL outputting the json data. Any help is appreciated.
You could use the JavaScriptSerializer class. You start by defining a model class which will hold the data. So let's suppose that the remote URL returns the following JSON:
{ name: 'John', addresses: [ { city: 'Paris' }, { city: 'London' } ] }
which could be represented by this model:
public class Person
{
public string Name { get; set; }
public Address[] Addresses { get; set; }
}
public class Address
{
public string City { get; set; }
}
And then deserialize the received JSON back to the model:
var serializer = new JavaScriptSerializer();
// TODO: Fetch the JSON from a remote URL
var json = "{name: 'foo', addresses: [{city: 'Paris'}, {city: 'London'}]}";
var person = serializer.Deserialize<Person>(json);
UPDATE:
In order to fetch the JSON from remote url you could use WebClient:
using (var client = new WebClient())
{
string json = client.DownloadString("http://someurl.com");
}
Here is what I have so far. A product of all answers that I get here in stack.
The idea is to get the json value from external web service and publish it in my controller as a json values and I dont have to create model for it. Hope this helps.
public class ApiJson: Controller
{
public JsonResult getUser()
{
WebClient client = WebClient();
NameValueCollection data = new NameValueCollection();
data.Add("param1", "value1");
byte[] result = client.UploadValues("http://localhost:9000/", data);
String json = Encoding.ASCII.GetString(result);
JavaScriptSerializer serializer = new JavaScriptSerializer();
dynamic item = serializer.Deserialize<object>(json);
return Json(item, JsonRequestBehavior.AllowGet);
}
}