The scenario of the problem is this
1) We map the struts field values to the dtos. The dtos contain integer fields which again are displayed on the screen.
2) Now I enter an incorrect value which gives conversion error for that integer field.
3) At that point in time I decide to quit the page(i.e press cancel), I get a conversion error. This is because the StrutsConversionErrorInterceptor gets called everytime.
Is there any way that I can skip the strutsConversionErrorInterceptor when I am calling a particular method the way we can skip validation using excludeMethods
Use this code to override Struts's StrutsConversionErrorInterceptor...
public class MyConversionErrorInterceptor extends AbstractInterceptor {
private static final long serialVersionUID = 1L;
public static final String ORIGINAL_PROPERTY_OVERRIDE = "original.property.override";
protected Object getOverrideExpr(ActionInvocation invocation, Object value) {
ValueStack stack = invocation.getStack();
try {
stack.push(value);
return "'" + stack.findValue("top", String.class) + "'";
} finally {
stack.pop();
}
}
#Override
public String intercept(ActionInvocation invocation) throws Exception {
ActionContext invocationContext = invocation.getInvocationContext();
Map<String, Object> conversionErrors = invocationContext.getConversionErrors();
ValueStack stack = invocationContext.getValueStack();
HashMap<Object, Object> fakie = null;
BaseAction baseAction = (BaseAction) invocation.getAction();
String buttonName = baseAction.getButtonName();
for (Map.Entry<String, Object> entry : conversionErrors.entrySet()) {
String propertyName = entry.getKey();
Object value = entry.getValue();
if (shouldAddError(propertyName, value)) {
String message = XWorkConverter.getConversionErrorMessage(propertyName, stack);
Object action = invocation.getAction();
if (action instanceof ValidationAware) {
ValidationAware va = (ValidationAware) action;
if(buttonName.equalsIgnoreCas("Next")){
va.addFieldError(propertyName, message);
}
}
if (fakie == null) {
fakie = new HashMap<Object, Object>();
}
if(buttonName.equalsIgnoreCas("Next")){
fakie.put(propertyName, getOverrideExpr(invocation, value));
}
}
}
if (fakie != null) {
// if there were some errors, put the original (fake) values in
// place right before the result
stack.getContext().put(ORIGINAL_PROPERTY_OVERRIDE, fakie);
invocation.addPreResultListener(new PreResultListener() {
public void beforeResult(ActionInvocation invocation, String resultCode) {
Map<Object, Object> fakie = (Map<Object, Object>) invocation.getInvocationContext().get(ORIGINAL_PROPERTY_OVERRIDE);
if (fakie != null) {
invocation.getStack().setExprOverrides(fakie);
}
}
});
}
return invocation.invoke();
}
protected boolean shouldAddError(String propertyName, Object value) {
if (value == null) {
return false;
}
if ("".equals(value)) {
return false;
}
if (value instanceof String[]) {
String[] array = (String[]) value;
if (array.length == 0) {
return false;
}
if (array.length > 1) {
return true;
}
String str = array[0];
if ("".equals(str)) {
return false;
}
}
return true;
}
}
You can specify you button names on which you want validation to fire. In above code I have used "Next" in code you can see
if(buttonName.equalsIgnoreCas("Next"))
Yes, you can skip calling the interceptor.
Just remove the interceptor definition from your action definition in struts.xml file.
i.e., remove <interceptor-ref name="conversionError"/>
Mainly this interceptor adds any error found in the ActionContext's conversionErrors map as a field error (provided that the action implements ValidationAware). In addition, any field that contains a validation error has its original value saved such that any subsequent requests for that value return the original value rather than the value in the action. This is important because if the value "abc" is submitted and can't be converted to an int, we want to display the original string ("abc") again rather than the int value (likely 0, which would make very little sense to the user).
After you removed this interceptor, if the struts failed to map the field with parameter of the object(i.e., from string to int), it throws result input action error.
This seems to be a better method to handle this scenario - using Conversion Validator. Repopulating Field upon conversion Error section is something very useful:
http://struts.apache.org/2.0.14/docs/conversion-validator.html
Related
I have an Entry which holds the Price and I wanted to format it as currency.
Here is the Entry tag
<Entry x:Name="Price" StyleId="Price"
Text="{Binding Model.Price, Converter={StaticResource CurrencyEntryFormatConverter}, Mode=TwoWay}"
Placeholder="{x:Static resx:Resources.PricePlaceholder}"
Style="{StaticResource DefaultEntry}" Keyboard="Numeric"/>
and here is the property in the Model
public decimal Price
{
get
{
return this.price;
}
set
{
if (this.price== value)
{
return;
}
this.price= value;
this.OnPropertyChanged();
}
}
Finally here is the converter:
public class CurrencyEntryFormatConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null || string.IsNullOrWhiteSpace(value.ToString()))
{
return value;
}
string result = string.Format(Resources.CurrencyFormatString, (decimal)value);
return result;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null || string.IsNullOrWhiteSpace(value.ToString()))
{
return 0;
}
string result = value.ToString().Replace(" ", "").Replace("$", "").Replace(",", "");
return result;
}
}
Question: My problem is that when I run the project and try to enter values in the Price field, the code repeats executing between Convert and ConvertBack functions of the converter and application hangs!
Any advice?
In my case the problem was the property implementation
If we define a property of a class which is implementing INotifyPropertyChanged, in order to update the view when the property value changes, we need to call OnPropertyChanged method in the set block:
public decimal Amount
{
get
{
return this.amount;
}
set
{
this.amount = value;
this.OnPropertyChanged(); // like this line
}
}
But just this code like so makes a loop with your bindings. So we need to be checking if the value is different than the current property's value and then if its new, update it. Look at this code:
public decimal Amount
{
get
{
return this.amount;
}
set
{
if (this.amount == value)
{
return;
}
this.amount = value;
this.OnPropertyChanged();
}
}
That if block helps you to stop looping between get and set.
I hope it helps someone.
I reproduced the issue I am having in a brand new MVC Web API project.
This is the default code with a slight modification.
public string Get(int? id, int? something = null)
{
var isValid = ModelState.IsValid;
return "value";
}
If you go to http://localhost/api/values/5?something=123 then this works fine, and isValid is true.
If you go to http://localhost/api/values/5?something= then isValid is false.
The issue I am having is that if you provide a null or omitted value for an item that is nullable, the ModelState.IsValid flags a validation error saying "A value is required but was not present in the request."
The ModelState dictionary also looks like this:
with two entries for something, one nullable, which I am not sure if it is significant or not.
Any idea how I can fix this so that the model is valid when nullable parameters are omitted or provided as null? I am using model validation within my web api and it breaks it if every method with a nullable parameter generates model errors.
It appears that the default binding model doesn't fully understand nullable types. As seen in the question, it gives three parameter errors rather than the expected two.
You can get around this with a custom nullable model binder:
Model Binder
public class NullableIntModelBinder : IModelBinder
{
public bool BindModel(System.Web.Http.Controllers.HttpActionContext actionContext, ModelBindingContext bindingContext)
{
if (bindingContext.ModelType != typeof(int?))
{
return false;
}
ValueProviderResult val = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (val == null)
{
return false;
}
string rawvalue = val.RawValue as string;
// Not supplied : /test/5
if (rawvalue == null)
{
bindingContext.Model = null;
return true;
}
// Provided but with no value : /test/5?something=
if (rawvalue == string.Empty)
{
bindingContext.Model = null;
return true;
}
// Provided with a value : /test/5?something=1
int result;
if (int.TryParse(rawvalue, out result))
{
bindingContext.Model = result;
return true;
}
bindingContext.ModelState.AddModelError(bindingContext.ModelName, "Cannot convert value to int");
return false;
}
}
Usage
public ModelStateDictionary Get(
int? id,
[ModelBinder(typeof(NullableIntModelBinder))]int? something = null)
{
var isValid = ModelState.IsValid;
return ModelState;
}
Adapted from the asp.net page: http://www.asp.net/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api for further reading and an alternative method to set it at the class(controller) level rather than per parameter.
This handles the 3 valid scenarios:
/test/5
/test/5?something=
/test/5?something=2
this first give "something" as null. Anything else (eg ?something=x) gives an error.
If you change the signature to
int? somthing
(ie remove = null) then you must explicitly provide the parameter, ie /test/5 will not be a valid route unless you tweak your routes as well.
You'll have to register a custom model-binder for nullable types as the default binder is calling the validator for nullable parameters as well, and the latter considers those empty values as invalid.
The Model Binder:
public class NullableModelBinder<T> : System.Web.Http.ModelBinding.IModelBinder where T : struct
{
private static readonly TypeConverter converter = TypeDescriptor.GetConverter( typeof( T ) );
public bool BindModel( HttpActionContext actionContext, System.Web.Http.ModelBinding.ModelBindingContext bindingContext )
{
var val = bindingContext.ValueProvider.GetValue( bindingContext.ModelName );
// Cast value to string but when it fails we must not suppress the validation
if ( !( val?.RawValue is string rawVal ) ) return false;
// If the string contains a valid value we can convert it and complete the binding
if ( converter.IsValid( rawVal ) )
{
bindingContext.Model = converter.ConvertFromString( rawVal );
return true;
}
// If the string does contain data it cannot be nullable T and we must not suppress this error
if ( !string.IsNullOrWhiteSpace( rawVal ) ) return false;
// String is empty and allowed due to it being a nullable type
bindingContext.ValidationNode.SuppressValidation = true;
return false;
}
}
Registration:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// ...
var provider = new SimpleModelBinderProvider(typeof(int?), new NullableModelBinder<int>());
config.Services.Insert(typeof(ModelBinderProvider), 0, provider);
// ...
}
}
Remove the default null value from the second parameter. The model binder will set it to null if it's something other than int.
I've found a working workaround for me (just exclude null values from data being sent - as opposed to sending values as nulls).
See https://stackoverflow.com/a/66712465/908608
Given a build definition, I extract the following pieces from it:
m_template = (DynamicActivity)WorkflowHelpers.DeserializeWorkflow(buildDefinition.Process.Parameters);
Properties = m_template.Properties.ToDictionary(p => p.Name, StringComparer.OrdinalIgnoreCase);
Metadata = WorkflowHelpers.GetCombinedMetadata(m_template).ToDictionary(m => m.ParameterName);
m_parameters = WorkflowHelpers.DeserializeProcessParameters(buildDefinition.ProcessParameters)
Now I wish to know the value of an arbitrary process parameter.
My current code is:
public ParameterValue GetParameterValue(string name)
{
object propValue;
var valueType = GetParameterType(name, out propValue);
object value;
if (!m_parameters.TryGetValue(name, out value))
{
value = propValue;
}
return new ParameterValue(valueType, value);
}
private Type GetParameterType(string name, out object value)
{
value = null;
if (Properties != null)
{
DynamicActivityProperty property;
if (Properties.TryGetValue(name, out property))
{
var inArgument = property.Value as InArgument;
if (inArgument != null)
{
if (inArgument.Expression != null)
{
var exprString = inArgument.Expression.ToString();
if (!exprString.StartsWith(": VisualBasicValue<"))
{
value = exprString;
}
}
return inArgument.ArgumentType;
}
if (property.Value != null)
{
value = property.Value;
return property.Value.GetType();
}
var typeName = property.Type.ToString();
if (typeName.StartsWith(IN_ARGUMENT_TYPE_NAME_PREFIX))
{
typeName = typeName.Substring(IN_ARGUMENT_TYPE_NAME_PREFIX.Length, typeName.Length - IN_ARGUMENT_TYPE_NAME_PREFIX.Length - 1);
return Type.GetType(typeName, true);
}
return property.Type;
}
}
return typeof(string);
}
Unfortunately, this code stumbles for parameters satisfying all of the following conditions:
The parameter value is wrapped as InArgument<T>.
T is a non primitive type, for example string[]
The build definition does not override the value inherited from the process template.
What happens is that:
Because the value is non primitive exprString.StartsWith(": VisualBasicValue<") and I do not know how to handle it. Hence propValue is null.
Because the value is not overridden by the build definition !m_parameters.TryGetValue(name, out value) and hence I just return propValue.
As a result my logic returns null. But it is wrong! For example, I have a string[] parameter which has a list of string in the process template, but my logic returns null for the reasons explained.
So, what is the proper way to compute it?
You can use the following code (included in another link) to get value and type of one process parameter:
TfsTeamProjectCollection tfctc = new TfsTeamProjectCollection(new Uri("http://tfsservername:8080/tfs/DefaultCollection"));
IBuildServer bs = tfctc.GetService<IBuildServer>();
IBuildDetail[] builds = bs.QueryBuilds("teamprojectname", "builddefinitionname");
foreach (var build in builds)
{
var buildefinition = build.BuildDefinition;
IDictionary<String, Object> paramValues = WorkflowHelpers.DeserializeProcessParameters(buildefinition.ProcessParameters);
string processParametersValue = paramValues["argument1"].ToString();
Console.WriteLine(processParametersValue);
}
Also have a check on this case: TFS 2010: Why is it not possible to deserialize a Dictionary<string, object> with XamlWriter.Save when I can use XamlReader for deserializing
I need to catch data from < itunes:sumary > tag but my handler is getting only the end of tag's content (last three words for example). I don't know what to do because other tags are being handled as expected, getting all content.*
I've seen that some tags are ignored by parser, but I don't think it's happening with because as I said it gets the content but only the end of that.
The source XML is hosted in -> http://djpaulonla.podomatic.com/archive/rss2.xml
Please, could someone help me???
The code is the following:
public class PodOMaticCustomHandler extends CustomHandler {
public PodOMaticCustomHandler(int quantityToFetch, String startTagValue,
String endTagValue) {
super(quantityToFetch, startTagValue, endTagValue);
}
#Override
public void characters(char[] ch, int start, int length)
throws SAXException {
super.characters(ch, start, length);
this.value = new String(ch, start, length);
}
#Override
public void endDocument() throws SAXException {
super.endDocument();
this.endDoc = true;
}
#Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
super.endElement(uri, localName, qName);
if (this.podcast != null) {
if (qName.equalsIgnoreCase("title")) {
podcast.setTitle(this.value);
} else if (qName.equalsIgnoreCase("pubDate")) {
podcast.setPubDate(this.value);
} else if (qName.equalsIgnoreCase("description")) {
podcast.setContent(this.value);
} else if (qName.equalsIgnoreCase("guid")) {
this.podcast.setLink(value);
}
}
}
#Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
super.startElement(uri, localName, qName, attributes);
if (this.startTagValue == null) {
this.startTagValueFound = true;
} else if (qName.equalsIgnoreCase("guid")
&& this.value.equalsIgnoreCase(this.startTagValue)) {
this.startTagValueFound = true;
}
if (this.endTagValue != null) {
if (qName.equalsIgnoreCase("guid")
&& this.value.equalsIgnoreCase(this.endTagValue)) {
this.endDoc = true;
}
}
if (!this.endDoc) {
if (this.quantityToFetch != this.podcasts.size()) {
if (this.startTagValueFound == true) {
if (qName.equalsIgnoreCase("item")) {
this.podcast = new Podcast();
} else if (qName.equalsIgnoreCase("enclosure")) {
this.podcast.setMedia(attributes.getValue("url"));
this.podcasts.add(podcast);
}
}
} else {
this.podcast = null;
}
}else{
this.podcast = null;
}
}
}
You can't rely on the characters method being called once with the entire element text, it may be called multiple times, each time with only part of the text.
Add a debug log statement to the characters method showing what you're setting value to and you will see that values is getting set with the first part of the string and then getting overwritten with the last part.
The answer is to buffer the text passed in from the characters calls in a CharArrayWriter or StringBuilder. Then you have to clear the buffer when the end of the element is found.
Here's what the Java tutorial on SAX has to say about the characters method:
Parsers are not required to return any particular number of characters at one time. A parser can return anything from a single character at a time up to several thousand and still be a standard-conforming implementation. So if your application needs to process the characters it sees, it is wise to have the characters() method accumulate the characters in a java.lang.StringBuffer and operate on them only when you are sure that all of them have been found.
I need to allow my content pipeline extension to use a pattern similar to a factory. I start with a dictionary type:
public delegate T Mapper<T>(MapFactory<T> mf, XElement d);
public class MapFactory<T>
{
Dictionary<string, Mapper<T>> map = new Dictionary<string, Mapper<T>>();
public void Add(string s, Mapper<T> m)
{
map.Add(s, m);
}
public T Get(XElement xe)
{
if (xe == null) throw new ArgumentNullException(
"Invalid document");
var key = xe.Name.ToString();
if (!map.ContainsKey(key)) throw new ArgumentException(
key + " is not a valid key.");
return map[key](this, xe);
}
public IEnumerable<T> GetAll(XElement xe)
{
if (xe == null) throw new ArgumentNullException(
"Invalid document");
foreach (var e in xe.Elements())
{
var val = e.Name.ToString();
if (map.ContainsKey(val))
yield return map[val](this, e);
}
}
}
Here is one type of object I want to store:
public partial class TestContent
{
// Test type
public string title;
// Once test if true
public bool once;
// Parameters
public Dictionary<string, object> args;
public TestContent()
{
title = string.Empty;
args = new Dictionary<string, object>();
}
public TestContent(XElement xe)
{
title = xe.Name.ToString();
args = new Dictionary<string, object>();
xe.ParseAttribute("once", once);
}
}
XElement.ParseAttribute is an extension method that works as one might expect. It returns a boolean that is true if successful.
The issue is that I have many different types of tests, each of which populates the object in a way unique to the specific test. The element name is the key to MapFactory's dictionary. This type of test, while atypical, illustrates my problem.
public class LogicTest : TestBase
{
string opkey;
List<TestBase> items;
public override bool Test(BehaviorArgs args)
{
if (items == null) return false;
if (items.Count == 0) return false;
bool result = items[0].Test(args);
for (int i = 1; i < items.Count; i++)
{
bool other = items[i].Test(args);
switch (opkey)
{
case "And":
result &= other;
if (!result) return false;
break;
case "Or":
result |= other;
if (result) return true;
break;
case "Xor":
result ^= other;
break;
case "Nand":
result = !(result & other);
break;
case "Nor":
result = !(result | other);
break;
default:
result = false;
break;
}
}
return result;
}
public static TestContent Build(MapFactory<TestContent> mf, XElement xe)
{
var result = new TestContent(xe);
string key = "Or";
xe.GetAttribute("op", key);
result.args.Add("key", key);
var names = mf.GetAll(xe).ToList();
if (names.Count() < 2) throw new ArgumentException(
"LogicTest requires at least two entries.");
result.args.Add("items", names);
return result;
}
}
My actual code is more involved as the factory has two dictionaries, one that turns an XElement into a content type to write and another used by the reader to create the actual game objects.
I need to build these factories in code because they map strings to delegates. I have a service that contains several of these factories. The mission is to make these factory classes available to a content processor. Neither the processor itself nor the context it uses as a parameter have any known hooks to attach an IServiceProvider or equivalent.
Any ideas?
I needed to create a data structure essentially on demand without access to the underlying classes as they came from a third party, in this case XNA Game Studio. There is only one way to do this I know of... statically.
public class TestMap : Dictionary<string, string>
{
private static readonly TestMap map = new TestMap();
private TestMap()
{
Add("Logic", "LogicProcessor");
Add("Sequence", "SequenceProcessor");
Add("Key", "KeyProcessor");
Add("KeyVector", "KeyVectorProcessor");
Add("Mouse", "MouseProcessor");
Add("Pad", "PadProcessor");
Add("PadVector", "PadVectorProcessor");
}
public static TestMap Map
{
get { return map; }
}
public IEnumerable<TestContent> Collect(XElement xe, ContentProcessorContext cpc)
{
foreach(var e in xe.Elements().Where(e => ContainsKey(e.Name.ToString())))
{
yield return cpc.Convert<XElement, TestContent>(
e, this[e.Name.ToString()]);
}
}
}
I took this a step further and created content processors for each type of TestBase:
/// <summary>
/// Turns an imported XElement into a TestContent used for a LogicTest
/// </summary>
[ContentProcessor(DisplayName = "LogicProcessor")]
public class LogicProcessor : ContentProcessor<XElement, TestContent>
{
public override TestContent Process(XElement input, ContentProcessorContext context)
{
var result = new TestContent(input);
string key = "Or";
input.GetAttribute("op", key);
result.args.Add("key", key);
var items = TestMap.Map.Collect(input, context);
if (items.Count() < 2) throw new ArgumentNullException(
"LogicProcessor requires at least two items.");
result.args.Add("items", items);
return result;
}
}
Any attempt to reference or access the class such as calling TestMap.Collect will generate the underlying static class if needed. I basically moved the code from LogicTest.Build to the processor. I also carry out any needed validation in the processor.
When I get to reading these classes I will have the ContentService to help.