2

I'm getting data in to parse and I have a generic parser that I have given instructions on a per field basis about the data type and format (if applicable) of the data.

For example:

<Field Name="DateTime" Type="Pattern" Expression="\d{8} \d{2}\:\d{2}\:\d{2}" DataType="System.DateTime" Format="yyyyMMdd HH:mm:ss" />

Type could be date, integer, decimal, any value type. Format could any date or numeric format.

Convert.ChangeType does not have an overload that just takes a custom format string. I tried to implement IFormatProvider but I don't know how to make that work.

So I'm trying to figure out how to make this method work in the most generic way possible.

  Public Function ConvertValue(Value As String, Type As System.Type, Format As String) As Object

  End Function

An example of date data would be "20141215 10:07:25" {String}, so I would just call ConvertValue("20141215 10:07:25", GetType(DateTime), "yyyyMMdd HH:mm:ss")

5
  • It appears that the XML at the start of your question is not data to be parsed, but some kind of parsing rule definition. What would the actual data look like, and how would it be fed to the ConvertValue method? Commented Dec 16, 2014 at 20:38
  • I don't understand what is the role of Expression and Type here? Commented Dec 16, 2014 at 20:46
  • Since there's a small number of classes that can be converted that way and since Parse do not implement an interface, you'll have to make a case for each possible type. You could instead try to desirialize the data. Commented Dec 16, 2014 at 20:54
  • @the_lotus, if you post an example of deserialize, I'll mark as answer. Thanks! Commented Dec 16, 2014 at 22:40
  • @KonradKokosa, they are for the parsing part, to get the text of that field out of the file. Not part of the question but relevant to the context in which the question is asked. Commented Dec 16, 2014 at 22:41

2 Answers 2

1

As long as you are going for a one size fits all function for all Types, you will have If/Case statements. I have a generic converter to help with types which cannot normally be serialized, but converting to a standalone function without the underpinnings makes it FUgly1:

Private Shared Function ConvertToT(Of T)(obj As String, fmt As String) As T

    If GetType(T) Is GetType(DateTime) Then

        Dim dt As DateTime
        DateTime.TryParseExact(obj, fmt, CultureInfo.CurrentCulture,
                               DateTimeStyles.None, dt)
        Return Convert.ChangeType(dt, GetType(T))

    ElseIf GetType(T) Is GetType(Decimal) Then
        Dim dec As Decimal
        If Decimal.TryParse(obj, dec) Then
            Return Convert.ChangeType(dec, GetType(T))
        Else
            Return Convert.ChangeType(Decimal.MinValue, GetType(T))    ' or exception
        End If

    ElseIf GetType(T) Is GetType(Double) Then
        ' etc etc etc

    End If

End Function

Since you need to use different functions for the conversion, you get a long Case/If block. Even when using something like ConvertFromInvariantString, you have the return type. A set of extensions seems to have less cruft and seems a bit more usable in this context:

Module ParseExtensions

    <Extension>
    Public Function ParseToDate(dt As DateTime, data As String, 
                   format As String) As DateTime
        Dim newDate As DateTime

        DateTime.TryParseExact(data, format, CultureInfo.CurrentCulture,
                               DateTimeStyles.None, newDate)
        Return newDate
    End Function

    <Extension>
    Public Function ParseToDecimal(dec As Decimal, 
                          data As String) As Decimal
        Dim newDec As Decimal

        If Decimal.TryParse(data, newDec) Then
            Return newDec
        Else
            Return Decimal.MinValue    ' or exception
        End If
    End Function

End Module

Usage and trial case:

enter image description here

1 Functional, but Ugly.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks. I will not have (Of Type) at compile time. And I would not be able to say ParseToDate without a Case statement. I guess there is no way around a Case, because if there were, you would know about it.
I will try overloaded extension methods (all with same name but taking diff types as argument). To see if that does the trick.
I like something like ParseFrom(string, format) nice generic approach/name. Are you generating that XML descriptor? ConvertToInvariantString makes for a wonderful base and no need for any format descriptors.
1
  Sub Test()
    Dim dte As Date = CDate(ConvertValue("20141215 10:07:25", GetType(DateTime), "yyyyMMdd HH:mm:ss"))
    MsgBox(dte.ToString)
    Dim i As Integer = CInt(ConvertValue("1234", GetType(Integer), "0000"))
    MsgBox(i.ToString)
  End Sub

  Public Function ConvertValue(Value As String, Type As System.Type, Format As String) As Object
    If Type Is GetType(Date) Then
      Dim dte As Date
      Dim enAU As New Globalization.CultureInfo("en-AU")
      If Not Date.TryParseExact(Value, Format, enAU, Globalization.DateTimeStyles.None, dte) Then
        dte = New Date
      End If
      Return dte
    ElseIf Type Is GetType(Integer) Then
      Dim i As Integer
      If Not Integer.TryParse(Value, i) Then
        i = 0
      End If
      Return i
    Else
      Return Nothing
    End If
  End Function

2 Comments

This is a good "Plan B". I un-downvoted it. I was looking for a caseless solution - Maybe there isn't one.
Thanks. I'd be interested too, but I think the format string syntax has evolved over decades and isn't strict enough for a general solution. Hopefully I'm wrong on that! Good luck :-)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.