Форматирование значений элементов / атрибутов с помощью XML-сериализации - PullRequest
3 голосов
/ 24 апреля 2009

У меня есть класс, который представляет данные кредитной карты. Для представления действительных месяцев и месяцев истечения срока действия я использую четыре свойства типа int:

public int ValidFromMonth { get; set; }
public int ValidFromYear { get; set; }
public int ExpiresEndMonth { get; set; }
public int ExpiresEndYear { get; set; }

Я выполняю XML-сериализацию этого класса для использования третьей стороной. Эта третья сторона требует, чтобы моим месяцам и годам предшествовал начальный ноль, если значение меньше 10

<validFromMonth>02</validFromMonth>
<validFromYear>09</validFromYear>
<expiresEndMonth>10</expiresEndMonth>
<expiresEndYear>14</expiresEndYear>

Поддерживает ли .NET какую-либо атрибуцию (или я могу создать собственный атрибут), который будет применять это правило, возможно, с использованием строки формата (например, {0:00})?

Примечание: я знаю, что мог бы добавить свои собственные свойства string, которые выполняют внутреннее форматирование, и добавить атрибут [XmlIgnore] в мои свойства int, но это похоже на второсортное решение.

Edit: После некоторого рассмотрения я задаюсь вопросом, действительно ли это просто не осуществимо. Сериализация не будет проблемой, но для того, чтобы десериализация работала, вам нужно отформатировать сериализованную строку. В приведенном выше тривиальном примере это было бы легко, но я не уверен, что это можно сделать для более общего случая.

Edit2: Схема XML, определяющая двухзначное требование, приведена ниже.

Простые определения типов:

<xs:simpleType name="CreditCardMonthType">
  <xs:annotation>
   <xs:documentation>Two digit month</xs:documentation>
  </xs:annotation>
  <xs:restriction base="xs:string">
   <xs:minLength value="2" />
   <xs:maxLength value="2" />
  </xs:restriction>
 </xs:simpleType>
<xs:simpleType name="CreditCardYearType">
  <xs:annotation>
   <xs:documentation>Two digit year</xs:documentation>
  </xs:annotation>
  <xs:restriction base="xs:string">
   <xs:minLength value="2" />
   <xs:maxLength value="2" />
  </xs:restriction>
</xs:simpleType>

Определение кредитной карты, которое использует эти типы:

<xs:attribute name="ExpiryMonth" type="CreditCardMonthType" use="required">
 <xs:annotation>
  <xs:documentation>Credit/debt card's expiry month.</xs:documentation>
 </xs:annotation>
</xs:attribute>
<xs:attribute name="ExpiryYear" type="CreditCardYearType" use="required">
 <xs:annotation>
  <xs:documentation>Credit/debt card's expiry year.</xs:documentation>
 </xs:annotation>
</xs:attribute>
<xs:attribute name="StartMonth" type="CreditCardMonthType" use="optional">
 <xs:annotation>
  <xs:documentation>Switch card's start month.</xs:documentation>
 </xs:annotation>
</xs:attribute>
<xs:attribute name="StartYear" type="CreditCardYearType" use="optional">
 <xs:annotation>
  <xs:documentation>Switch card's start year.</xs:documentation>
 </xs:annotation>
</xs:attribute>

Ответы [ 4 ]

3 голосов
/ 26 апреля 2009

ОК, игнорируйте мой предыдущий пример кода (я оставлю его, так как он может помочь кому-то еще, хотя). Я только что вспомнил, что вы можете сделать это, используя XmlEnumAttribute :

public enum LeadingZeroMonth
{
    [XmlEnum("01")]
    January,

    ...

    [XmlEnum("12")]
    December
}

и затем измените ваше использование на enum:

public LeadingZeroMonth ValidFromMonth { get; set; }

Это на самом деле очень хороший способ, так как у вас теперь есть перечисление за месяц (что на самом деле вы должны были сделать с самого начала).

3 голосов
/ 24 апреля 2009

Это много кода, но он делает то, что вы хотите. Суть в том, что вы можете создать новый класс (LeadingZero в этом примере) и реализовать IXmlSerializable, чтобы контролировать, как вы читаете / записываете из потока XML. Надеюсь, это поможет:

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

namespace StackOverflow
{
    [Serializable]
    public class LeadingZero : IXmlSerializable
    {
        public int Value { get; set; }

        public LeadingZero()
        {
            Value = 0;
        }

        public LeadingZero(int value)
        {
            this.Value = value;
        }

        public override string ToString()
        {
            return Value.ToString("00");
        }

        #region IXmlSerializable Members

        public System.Xml.Schema.XmlSchema GetSchema()
        {
            return null;
        }

        public void ReadXml(System.Xml.XmlReader reader)
        {
            string s = reader.ReadElementString();
            int i;
            if (int.TryParse(s, out i))
            {
                Value = i;
            }
        }

        public void WriteXml(System.Xml.XmlWriter writer)
        {
            writer.WriteString(Value.ToString("00"));
        }

        #endregion
    }

    [Serializable]
    public class Complex
    {
        public LeadingZero ValidFromMonth { get; set; }
        public LeadingZero ValidFromYear { get; set; }
        public LeadingZero ExpiresEndMonth { get; set; }
        public LeadingZero ExpiresEndYear { get; set; }
    }

    class Program
    {
        static void Main()
        {
            var seven = new LeadingZero(7);

            XmlSerializer xml = new XmlSerializer(typeof(LeadingZero));

            StringWriter writer;

            writer = new StringWriter();
            xml.Serialize(writer, seven);

            string s = writer.ToString();

            Console.WriteLine(seven);
            Console.WriteLine();
            Console.WriteLine(s);

            Console.WriteLine();
            var newSeven = xml.Deserialize(new StringReader(s)) as LeadingZero;
            Console.WriteLine(newSeven ?? new LeadingZero(0));

            var complicated = new Complex()
            {
                ValidFromMonth = new LeadingZero(7),
                ValidFromYear = new LeadingZero(2009),
                ExpiresEndMonth = new LeadingZero(6),
                ExpiresEndYear = new LeadingZero(2010)
            };

            Console.WriteLine();
            writer = new StringWriter();

            xml = new XmlSerializer(typeof(Complex));
            xml.Serialize(writer, complicated);
            s = writer.ToString();
            Console.WriteLine(s);

            var newComplicated = xml.Deserialize(new StringReader(s)) as Complex;
            if (newComplicated != null)
            {
                Console.WriteLine();
                Console.WriteLine("Woo hoo!");
            }

            Console.ReadLine();
        }
    }
}

Это вывод, который я получил:

07

<?xml version="1.0" encoding="utf-16"?>
<LeadingZero>07</LeadingZero>

07

<?xml version="1.0" encoding="utf-16"?>
<Complex xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http:/
/www.w3.org/2001/XMLSchema">
  <ValidFromMonth>07</ValidFromMonth>
  <ValidFromYear>2009</ValidFromYear>
  <ExpiresEndMonth>06</ExpiresEndMonth>
  <ExpiresEndYear>2010</ExpiresEndYear>
</Complex>

Woo hoo!
1 голос
/ 30 апреля 2009

Недостаток использования XmlEnum в том, что он не может быть обнуляемым

Я буду рекомендовать

    [XmlIgnore]
    private int? _startMonth;

    /// <remarks/>
    [XmlAttributeAttribute]
    public string StartMonth
    {
        get { return _startMonth == null ? null : _startMonth.ToString().PadLeft(2, '0'); }
        set { _startMonth = string.IsNullOrEmpty(value) ? (int?)null : int.Parse(value); }
    }

Это позволит вам сделать атрибут обнуляемым

1 голос
/ 27 апреля 2009

Такого рода требования часто исходят от компаний, которые не понимают XML. Вместо того, чтобы предполагать, что это именно тот случай, я спрошу: предоставили ли они вам XML-схему, которая описывает формат дня с начальным нулем? Если да, не могли бы вы опубликовать ту часть, которая определяет день?


РЕДАКТИРОВАТЬ на основе редактирования

Спасибо за публикацию схемы. Это подтвердило другое, что меня беспокоило. Ваши целые числа не являются целыми числами. Обратите внимание на <restriction base="xs:string"/>. Это строки, а не целые числа.

...