简体   繁体   English

解析 Xml 文档中的指数值

[英]Parse exponential Value in Xml document

I have an issue when deserializing an object (which I cannot modify).反序列化对象(我无法修改)时遇到问题。 I receive an exponential value for certain xml element and for represent them in my class I used decimal value expect that when I deserialize the xml document it fails.我收到某个 xml 元素的指数值,为了在我的类中表示它们,我使用十进制值期望当我反序列化 xml 文档时它失败。

<fwdRate>-9.72316862724032E-05</fwdRate>

Is there any solution to represent this attribute other than create 2 attributes in my class to represent it (one string and the other a decimal value)?除了在我的类中创建 2 个属性来表示它(一个字符串,另一个是十进制值)之外,是否有任何解决方案来表示此属性?

Can I create a custom deserializtion class for decimal value?我可以为十进制值创建自定义反序列化类吗?

        private void ParseXML(string value)
        {
            XmlSerializer serializer = new XmlSerializer(typeof(SwapDataSynapseResult));
            using (TextReader reader = new StringReader(value))
            {
                _result = serializer.Deserialize(reader) as SwapDataSynapseResult;
            }
        }

As Demand作为需求

using System;
using System.IO;
using System.Xml.Serialization;

[XmlRoot(ElementName = "result")]
public class Result
{
    [XmlElement(ElementName = "fwdRate")]
    public decimal FwdRate { get; set; }
}       
public class Program
{
    public static void Main()
    {
         string val = "<result><fwdRate>-9.72316862724032E-05</fwdRate></result>";
         Result response = ParseXML(val);
  }
  static Result ParseXML(string value)
  {
      XmlSerializer serializer = new XmlSerializer(typeof(Result));
      using (TextReader reader = new StringReader(value))
      {
         return serializer.Deserialize(reader) as Result;
      }
  }

}

In XML, decimal values are not allowed to use scientific (exponential) notation (See this link at the 'Restrictions' paragraph).在 XML 中,十进制值不允许使用科学(指数)表示法(请参阅“限制”段落中的此链接)。

Either:任何一个:

  • the value is indeed a floating point one: Put a float / double instead of a decimal in the code.该值确实是一个浮点数:在代码中放置一个float / double而不是decimal
  • the XML is corrupted. XML 已损坏。

In the same way, in C#, by default, Decimal.Parse doesn't accept exponential representation.同样,在 C# 中,默认情况下, Decimal.Parse不接受指数表示。 You can override this behavior by implementing a new struct that wrap a decimal and implement IXmlSerializable and allow exponential representation when de-serialized:您可以通过实现一个包含decimal并实现IXmlSerializable并在反序列化时允许指数表示的新结构来覆盖此行为:

public struct XmlDecimal : IXmlSerializable
{
    public decimal Value { get; private set; }

    public XmlDecimal(decimal value) => Value = value;

    public XmlSchema GetSchema() => null;

    public void ReadXml(XmlReader reader)
    {
        var s = reader.ReadElementContentAsString();
        Value = decimal.TryParse(s, NumberStyles.Number | NumberStyles.AllowExponent,
            NumberFormatInfo.InvariantInfo, out var value)
            ? value
            : 0; // If parse fail the resulting value is 0. Maybe we can throw an exception here.
    }

    public void WriteXml(XmlWriter writer) => writer.WriteValue(Value);

    public static implicit operator decimal(XmlDecimal v) => v.Value;

    public override string ToString() => Value.ToString();
}

The flaw is that you have to use this struct instead of a decimal everywhere in your model.缺陷是您必须在模型中的任何地方都使用此结构而不是decimal

And sadly you can't make this struct read-only, has explained here .遗憾的是,您不能将此结构设置为只读,已在此处进行了解释。

The proper way is to control how your properties are deserialized by implementing the IXmlSerializable interface:正确的方法是通过实现 IXmlSerializable 接口来控制属性的反序列化方式:

IXmlSerializable IXmlSerializable

IXmlSerializable code project example IXmlSerializable 代码项目示例

In the ReadXml method you should convert your number在 ReadXml 方法中,您应该转换您的号码

var yourStringNumber = ...

this.fwdRate = Decimal.Parse(yourStringNumber, System.Globalization.NumberStyles.Float);

But this method will require you to parse of the whole xml manually that is a bit overhead sometimes.但是这种方法需要您手动解析整个 xml,这有时会带来一些开销。

A simple solution (that smells but might be useful) is just to add additional fwdRateDecimal field to your class and fulfill the value after the serialization.一个简单的解决方案(闻起来但可能有用)只是向您的类添加额外的 fwdRateDecimal 字段并在序列化后实现该值。

 private void ParseXML(string value)
    {
        XmlSerializer serializer = new XmlSerializer(typeof(SwapDataSynapseResult));
        using (TextReader reader = new StringReader(value))
        {
            _result = serializer.Deserialize(reader) as SwapDataSynapseResult;
            _result.fwdRateDecimal = Decimal.Parse(_result.fwdRate, System.Globalization.NumberStyles.Float)
        }
    }

Also conversion can be implemented in a type directly:也可以直接在类型中实现转换:

[XmlRoot(ElementName = "result")]
public class Result
{
    [XmlElement(ElementName = "fwdRate")]
    public string FwdRateStr { get; set; }

    private string lastParsedValue = null;
    private decimal fwdRate = 0;

    [XmlIgnore]
    public decimal FwdRate 
    { 
     get 
     {
        if(FwdRateStr != lastParsedValue)
        {
             lastParsedValue = FwdRateStr
             fwdRate = Decimal.Parse(FwdRateStr ,System.Globalization.NumberStyles.Float)
        }

        return fwdRate
     }  
}

This is the solution:这是解决方案:

using System;
using System.IO;
using System.Xml.Serialization;

[XmlRoot(ElementName = "result")]
public class Result
{
    [XmlElement(ElementName = "fwdRate")]
    public double FwdRate { get; set; }
}
public class Program
{
    public static void Main()
    {
        string val = "<result><fwdRate>-9.72316862724032E-05</fwdRate></result>";
        Result response = ParseXML(val);
        Console.WriteLine(response.FwdRate);
    }
    static Result ParseXML(string value)
    {
        XmlSerializer serializer = new XmlSerializer(typeof(Result));
        using (TextReader reader = new StringReader(value))
        {
            return serializer.Deserialize(reader) as Result;
        }
    }

}

You can't modify the incoming xml but you can modify how you read its' data.您不能修改传入的 xml,但可以修改读取其数据的方式。

Read that number as a double instead of decimal and you will have the right decimal precision.将该数字读取为double精度而不是decimal ,您将获得正确的小数精度。

Output:输出:

-9,72316862724032E-05 -9,72316862724032E-05

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM