JJR
JJR

Reputation: 773

Format decimal for use in XML and ignore culture

We need to insert decimal values into a XML document (done by a XML writer) which works fine. First we used XmlConvert.ToString(myDecimalValue) which printed the value with four digits. Now we are forced to use always two digits and we tried to change this but with no luck so far.

First we dropped the use of XmlConvert because we found not overload that allows us to format the decimal so we tried myDecimalValue.ToString("0.00") which prints only two digits but replaces the . with a , which is not valid in our case.

So how can we format a decimal like 25000.00m to a string like 25000.00? (Rounding does not matter because the values never have more then two digits set)

EDIT

decimal m_nAmt;
public decimal Amount
{
    get
    {
        return m_nAmt;
    }
    set
    {
        if ((value < 0M) || (value > 999999999.99M))
        {
            throw new ArgumentOutOfRangeException();
        }
        if (SepaUtil.DecimalPlaces(value) > 2)
        {
            throw new ArgumentException();
        }

        m_nAmt = value;
    }
}

And where is is written

// the original method
XmlConvert.ToString(m_nAmt);

Both methods are member of the same class;

Here is a example of a value written into the Xml file

<InstdAmt Ccy="EUR">3360.0000</InstdAmt>

Edit 2

A example where a node get written. I used the Immediate window to get the current value of the ControlSum Property (System.Decimal) and the value written by XmlConvert. The result is that XmlConvert does write four decimal places although I cannot confirm that when using XmlConvert with a decimal variable in LINQPad. Where one difference is that this library uses .net 2.0

enter image description here

My Conclusion & Solution

It seems that the XmlConvert.ToString() or decimalvalue.ToString() applies four decimal places because the original value had four 0 applied which got ignored by the SepaUtil.DecimalPlaces(value) check (because 0 get ignored) and are not shown within the "Immediate Window" of VS2012. So I changed

 m_nAmt = value;

to

m_nAmt = Math.Round(value,2);

to ensure always two decimal places. This way will add missing decimal places and truncate if there are more.

Upvotes: 2

Views: 6131

Answers (2)

PMF
PMF

Reputation: 17288

Use

myValue.ToString("0.00", CultureInfo.InvariantCulture);

This will always write the value in a language-independent format (basically English). Use the same argument in any Parse() or TryParse() calls when reading.

As a general rule, all Parse(), TryParse() or ToString() calls should have the CultureInfo argument specified, either as CultureInfo.InvariantCulture or CultureInfo.CurrentCulture. This prevents any strange bugs that happen when the users language uses a different decimal mark than the developers.

Upvotes: 5

Jon Skeet
Jon Skeet

Reputation: 1502816

Assuming this really is decimal, I suggest you round the decimal value first:

decimal rounded = Math.Round(original, 2);
string roundedText = XmlConvert.ToString(rounded);

Obviously you can specify appropriate rounding/truncation behaviour, too.

While you can just specify the invariant culture instead as per PMF's answer, I would consider the above to be clearer in what you're trying to achieve:

  • You're specifically rounding the value
  • You're specifically using an XML conversion

One difference, however - in my version above, if the original number only has one decimal place (or none), you'll end up with (say) "15.5". If you explicitly format the string with the invariant culture, you'll end up with "15.50". If you definitely always want exactly 2 decimal places (no fewer) then PMF's answer is a more suitable one.

Upvotes: 3

Related Questions