Retrieve FormatException Argument - c#

I'm using a closed-source third-party library like this:
object val = SomeClass.ExtractValue( someObject );
Now somewhere further down the road, the third-party library tries to parse a DateTime value that has an unexpected format and throws a FormatException.
In this case, I would like to retrieve the string that it hasn't succeeded to parse and try to parse it myself.
Something like this:
object val;
try
{
val = SomeClass.ExtractValue( someObject );
}
catch( FormatException e )
{
string failed = e.GetParseArgument( );
val = DateTime.Parse( failed + " 2010" );
}
Yes, simply appending the year is pretty pointless, but you get the idea.
The third-party library doesn't support all formats I need, but I can't easily get the data from "someObject" either.
(Yes, I could try to replicate what the library does using Reflector, but I'd like to avoid that.)
Is there any way to do this?
Thanks.

Since someObject is an IDataReader, you could create a decorator and pass that into ExtractValue. You could then intercept the date string and modify the format before it gets passed to the library e.g.
public class DateFormattingDataReader : IDataReader
{
private readonly IDataReader inner;
public DateFormattingDataReader(IDataReader inner)
{
this.inner = inner;
}
public string GetString(int index)
{
string s = this.inner.GetString(index);
if(index == problematicColumnIndex)
{
//try to parse string and then format it for the library
}
else return s;
}
}
Alternatively you could record all values read from the reader, then you can get the failing data as the last read item and try to parse it yourself.

Related

Advanced string formatter with pattern support in C#

I would like to have a flexible template that can translate cases similar to:
WHnnn => WH001, WH002, WH003... (nnn is just a number indicated 3 digits)
INVyyyyMMdd => INV20220228
ORDERyyyyMMdd-nnn => ORDER20220228-007
I know that I can use the following code to achieve a specific template:
string.Format("INV{0:yyyy-MM-dd}", DateTime.Now)
Which should have the same result as case 2 above. But that's not flexible. As the customer may customize their own template as long as I can understand/support, like the third case above.
I know even for the third case, I can do something like this:
string.Format("ORDER{0:yyyy-MM-dd}-{1:d3}", DateTime.Now, 124)
But that's clumsy, as I would like the template (input) to be just like this:
ORDERyyyyMMdd-nnn
The requirement is to support all the supported patterns by string.Format in C#, but the template can be any combination of those patterns.
I would probably use a custom formatter for this case.
Create a new class that will contain date/time & number and will implement IFormattable interface.
There is one tip: use some internal format in style INV{nnn} or INV[nnn] where only the part in {} or [] will be replaced with the value.
Otherwise there could be unwanted changes like in Inv contains 'n'. You could get output as I7v.
In your examples the N is upper case, but will it be the case even after each customisation?
Code (simplified version):
internal sealed class InvoiceNumberInfo : IFormattable
{
private static readonly Regex formatMatcher = new Regex(#"^(?<before>.*?)\[(?<code>\w+?)\](?<after>.*)$");
private readonly DateTime date;
private readonly int number;
public InvoiceNumberInfo(DateTime date, int number)
{
this.date = date;
this.number = number;
}
public string ToString(string format, IFormatProvider formatProvider)
{
var output = format;
while (true)
{
var match = formatMatcher.Match(output);
if (!match.Success)
{
return output;
}
output = match.Groups["before"].Value + FormatValue(match.Groups["code"].Value) + match.Groups["after"].Value;
}
}
private string FormatValue(string code)
{
if (code[0] == 'n')
{
var numberFormat = "D" + code.Length.ToString(CultureInfo.InvariantCulture);
return this.number.ToString(numberFormat);
}
else
{
return this.date.ToString(code);
}
}
}
Use:
internal static class Program
{
public static void Main(string[] args)
{
if (args.Length == 0)
{
Console.WriteLine("No format to display");
return;
}
var inv = new InvoiceNumberInfo(DateTime.Now, number: 7);
foreach (string format in args)
{
Console.WriteLine("Format: '{0}' is formatted as {1:" + format + "}", format, inv);
}
}
}
And output:
Format: 'WH[nnn]' is formatted as WH007
Format: 'INV[yyyyMMdd]' is formatted as INV20220227
Format: 'ORDER[yyyyMMdd]-[nnn]' is formatted as ORDER20220227-007
NOTE This is only a simplified version, proof of concept. Use proper error checking for your code.

In C# string how to replace two chararacters ignoring a number

I am editing a serialized string becuase when deserilized it gives a parse error.
so from a long Serialized string I want to edit "myVar\": \"0.2 mm" with "myVar\": \"0.2"
if I use the follwoing code it works
string NewString = Serializedstring.Replace($"myVar\": \"0.2 mm", $"myVar\": \"0.2")
but my 0.2 is a varible that may change with every occurance. so all I want is to remove mm from the string "myVar\": \"0.2 mm"
If your JSON is coming in with two different formats, rather than trying to hack the JSON string into something usable, it is much safer to use a custom JsonConverter. For example:
public class MillimetreJsonConverter : JsonConverter<double>
{
public override double Read(ref Utf8JsonReader reader, Type typeToConvert,
JsonSerializerOptions options)
{
// First try to get a double, if it works then simply return it
if(reader.TryGetDouble(out var val))
{
return val;
}
// Otherwise we get the string value e.g. "0.2 mm" and
// do some simple string manipulation on it
var value = reader.GetString()!;
value = value.Replace(" mm", "");
if(double.TryParse(value, out var result))
{
return result;
}
// If we get here, perhaps we should throw an exception?
return 0;
}
public override void Write(Utf8JsonWriter writer, double value,
JsonSerializerOptions options)
{
// You can fill this in if you need it
throw new NotImplementedException();
}
}
Now you can modify the class to deserialise into, assuming your JSON looks like this {"myVar": "0.2 mm"},:
public class Foo
{
[JsonConverter(typeof(MillimetreJsonConverter))]
public double myVar { get; set; }
}
Finally, it's simple to deserialise:
var foo = JsonSerializer.Deserialize<Foo>(json);
I can recommend 2 alternate approaches
1)match what you can.
var str = NewString.Replace(" mm,\"", "");
if your string is not going to have a space followed by mm" anywhere else that should be fine.
2)A safer option would be to deserialize it into something that can handle it (i assume your deserializing that property to a number currently) and then string replace the property and map it to what you need it.
There are lots of ways to do this, RegEx, String tokenisation, char array searching
RegEx is probably the closest to what you are currently doing
the Regular expression syntax is described here Basics of RegEx
the pattern [0-9.]+ should match an decimal number but be warned it will also match anything that inclues numbers and dots such as ip addresses
so if you have a regex of
Regex rx = new Regex(#"([0-9.]+) mm")
<your string> = rx.Replace(<Your string>, #"$1");
the details are :
[0-9.] any number or dot
+ one or more of what ever preceded it
() a group that is of special interest
$1 the number of the group that you want to replace the match with
note $0 is the entire input string
this will replace any string that is (number) mm with just the value of the number

C# string pairing method (external file)

I’m writing a windows service C# service.
Within it I need a piece of functionality that can take a value (string) and look to an external file to try and match it and return the matching value, also a string.
So for example, the file might have:
Stringone – anothertwo
So if Stringone was passed the return value would be anothertwo
I need to it to be quite quick and importantly I must be able to make changes without having the recompile, so putting it into the resource file is not really an option.
Going to be less than 100 entries. So DB seems overkill.
Does anyone have some suggestions of what might be the best way to accomplish this?
Thanks.
Why dont you try XML or JSON? Create Serializable class.
Like
public class Entries
{
[JsonMember]
public string key{get;set;}
[JsonMember]
public string value{get;set;}
}
and use its collection to read.
Since it's a very small amount of entries, I think the easiest thing would be to create a dictionary<string, string>, serialize to json (since it's simple), and in the start method of the service just deserialize it.
Then, when your service gets a request, all you have to do is something like this:
public string GetTranslation(string source)
{
string returnValue = "";
_Dictionary.TryGetValue(source, out returnValue);
return returnValue; // if TryGetValue fails, you simply return an empty string
}
Or, you can return the source if there is no translation:
public string GetTranslation(string source)
{
string returnValue = "";
return _Dictionary.TryGetValue(source, out returnValue) ? source, returnValue;
}
Either way it's going to be super quick.

How can the parameter for string.format cut the string

I need to make a possibility to open a webpage either with an additional parameter or without one. If it has a parameter, I'll add it with the string.format function. Sometimes I need to format this parameter before opening the url, however, I don't want to make the code too specific.
The Parameter is always a string and I would need to cut the end of this string. Is it possible to do something in the string to be formatted to cut the unneeded text? If so how would the {0} have to look that it works?
string url = "http://foo.bar/xt:{0}";
string parameter = "abcdefghi";
if (Regex.Matches(Regex.Replace(url,
#"(\{{2}|\}{2})", ""), // removes escaped curly brackets
#"\{(\d+)(?:\:?[^}]*)\}")
.OfType<Match>().Any())
{
Process.Start(string.Format(url, parameter));
}
else
{
Process.Start(url);
}
Instead of abcdefghi I would like to have as parameter just abcdefg for instance. But this should be configurable via url. Something like {0:7) or so...
You have to write this kind of logic yourself. What you can do, however, is wrap this in an extension method so you don't have to duplicate it everywhere:
public static class StringExt
{
public static string Truncate(this string value, int maxLength)
{
if (string.IsNullOrEmpty(value)) return value;
return value.Length <= maxLength ? value : value.Substring(0, maxLength);
}
}
Now we can write:
var someString = "...";
someString = someString.Truncate(2);

String.Format() not invoking custom formatter

I have a custom formatter specified, basically like this:
public class NotationNumericFormatter : IFormatProvider, ICustomFormatter
{
public object GetFormat(Type formatType_)
{
return (formatType_ == typeof(ICustomFormatter) ? this : null;
}
public string Format(string format_, object arg_, IFormatProvider formatProvider_)
{
if (!Equals(formatProvider_) || arg_ == null) // <-- I put a breakpoint here...
{
return;
}
// then a bunch of stuff happens here.
}
}
What's stumping me at the moment is, the following code:
// _myFormatter is a NotationNumericFormatter which gets instanced
// in the ctor of the class in question.
var result = string.Format(_myFormatter, (parameter_ ?? "").ToString(), value_);
Which is never, ever, hitting the first line in my formatter's Format() method. What am I missing here? Is there some subtlety to string.Format that I'm missing?
If parameter does not have {0} then the formatter won't break point
this will breakpoint
var result = string.Format(_myFormatter, "{0}", value_);
this won't
var result = string.Format(_myFormatter, "", value_);
When you call String.Format(IFormatProvider provider , String format , params Object[] args ) the second parameter format hast to specified as a valid format string i.e. it cannot be empty.
Try to set parameter_ to something like "{0}" then it should work.

Categories