web-dev-qa-db-ja.com

C#Xmlのシリアル化と逆シリアル化

オブジェクトをシリアル化して、SQL Server 2008xmlフィールドに保存しようとしています。また、オブジェクトを再水和する逆シリアル化コードもあります。オブジェクトをシリアル化してデータベースに保存することはできますが、「ルート要素がありません」という例外が発生します。

[XmlRoot("Patient")]
public class PatientXml
{
    private AddressXml _address = null;
    private EmergencyContactXml _emergencyContact = null;
    private PersonalXml _personal = null;

    [XmlElement]
    public PersonalXml Personal
    {
        get { return _personal; }
        set { _personal = value; }
    }

    [XmlElement]
    public AddressXml Address
    {
        get { return _address; }
        set { _address = value; }
    }

    [XmlElement]
    public EmergencyContactXml EmergencyContact
    {
        get { return _emergencyContact; }
        set { _emergencyContact = value; }
    }

    public PatientXml(){}
    public PatientXml(Patient patient)
    {
        _address = new AddressXml(patient.Address);
        _emergencyContact = new EmergencyContactXml(patient.EmergencyInfo);
        _personal = new PersonalXml(patient);
    }
}

public class PersonalXml
{
    private string _firstName = string.Empty, _lastName = string.Empty, _dateOfBirth = string.Empty, _phone = string.Empty;

    [XmlAttribute]
    public string FirstName
    {
        get { return _firstName; }
        set { _firstName = value; }
    }

    [XmlAttribute]
    public string LastName
    {
        get { return _lastName; }
        set { _lastName = value; }
    }

    [XmlAttribute]
    public string DateOfBirth
    {
        get { return _dateOfBirth; }
        set { _dateOfBirth = value; }
    }

    [XmlAttribute]
    public string Phone
    {
        get { return _phone; }
        set { _phone = value; }
    }

    public PersonalXml(){}
    public PersonalXml(Patient patient)
    {
        _firstName = patient.FirstName;
        _lastName = patient.LastName;
        _dateOfBirth = patient.DateOfBirth.ToShortDateString();
        _phone = patient.Phone;
    }
}

public class AddressXml
{
    private string _address1 = string.Empty, _address2 = string.Empty, _city = string.Empty, _state = string.Empty, _Zip = string.Empty;

    [XmlAttribute]
    public string Address1
    {
        get { return _address1; }
        set { _address1 = value; }
    }

    [XmlAttribute]
    public string Address2
    {
        get { return _address2; }
        set { _address2 = value; }
    }

    [XmlAttribute]
    public string City
    {
        get { return _city; }
        set { _city = value; }
    }

    [XmlAttribute]
    public string State
    {
        get { return _state; }
        set { _state = value; }
    }

    [XmlAttribute]
    public string Zip
    {
        get { return _Zip; }
        set { _Zip = value; }
    }

    public AddressXml(){}
    public AddressXml(Address address)
    {
        _address1 = address.Address1;
        _address2 = address.Address2;
        _city = address.City;
        _state = address.State;
        _Zip = address.ZipCode;
    }
}

public class EmergencyContactXml
{
    private string _name = string.Empty, _phone = string.Empty, _relationship = string.Empty;

    [XmlAttribute]
    public string Name
    {
        get { return _name; }
        set { _name = value; }
    }

    [XmlAttribute]
    public string Phone
    {
        get { return _phone; }
        set { _phone = value; }
    }

    [XmlAttribute]
    public string Relationship
    {
        get { return _relationship; }
        set { _relationship = value; }
    }

    public EmergencyContactXml(){}
    public EmergencyContactXml(EmergencyContact contact)
    {
        _name = contact.ContactName;
        _phone = contact.Phone;
        _relationship = contact.Relationship;
    }
}

シリアル化されたXml出力:

<Patient 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <Personal FirstName="Test" LastName="User 1" DateOfBirth="3/13/1966" Phone="6304449866" />
    <Address Address1="123 Some St" City="Bartlett" State="CT" Zip="60111" />
    <EmergencyContact Name="Dr Chanduwarthana" Phone="6309769484" Relationship="Father" />
</Patient>

シリアル化と逆シリアル化のコード:

public static class XmlSerializer
{
    public static string Serialize<T>(T item)
    {
        MemoryStream memStream = new MemoryStream();
        using (XmlTextWriter textWriter = new XmlTextWriter(memStream, Encoding.Unicode))
        {
            System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(T));
            serializer.Serialize(textWriter, item);

            memStream = textWriter.BaseStream as MemoryStream;
        }
        if (memStream != null)
            return Encoding.Unicode.GetString(memStream.ToArray());
        else
            return null;
    }

    public static T Deserialize<T>(string xmlString)
    {
        if (string.IsNullOrWhiteSpace(xmlString))
            return default(T);

        using (MemoryStream memStream = new MemoryStream())
        {
            using (XmlTextWriter textWriter = new XmlTextWriter(memStream, Encoding.Unicode))
            {
                memStream.Position = 0;
                System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(T));
                return (T)serializer.Deserialize(memStream);
            }
        }
    }
}
11
Skadoosh

デシリアライズコードでは、MemoryStreamとXmlTextWriterを作成していますが、デシリアライズする文字列を指定していません。

using (MemoryStream memStream = new MemoryStream())
{
    using (XmlTextWriter textWriter = new XmlTextWriter(memStream, Encoding.Unicode))
    {
        // Omitted
    }
}

バイトをメモリストリームに渡し、XmlTextWriterを完全に廃止することができます。

using (MemoryStream memStream = new MemoryStream(Encoding.Unicode.GetBytes(xmlString)))
{
    System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(T));
    return (T)serializer.Deserialize(memStream);
}
11
Dave Fancher

XMLへのシリアル化を理解したようですので、私のアドバイスに従って、XMLを特殊なフィールドではなく文字列フィールド(varchar、nvarchar、text、ntext)に格納してください。

その小さな切り替えを行うと、準備が整います...それ以上の変更は必要ありません。

XMLフィールドは検証の対象であり、いくつかの頭痛の種があります。アプリケーションがそのフィールドのプロデューサーとコンシューマーのみである場合は、そのショートカットを使用することをお勧めします。 SQL2008(2005でも)は、xmlフィールドをコンパイルすることで節約できるリソースを補うのに十分な強度があります。

ただし、コードを少し最適化すると、必要以上に多くのコードを記述したように見えます。たとえば、プロパティのデータを保存するためにプライベートフィールドを作成する必要はなくなりました。次に例を示します。

public PersonalXml Personal
{
    get { return _personal; }
    set { _personal = value; }
}

あなたがそれを次のように書いた場合、うまくいくでしょう:

    public PersonalXml Personal { get ; set ; }

あなたが切ることができたより多くの脂肪があります...

1

XMLヘッダーを追加する必要があると思います。

<?xml version="1.0" encoding="utf-8" ?>

シリアル化メソッドを変更して、これを追加するオプションのパラメーターを受け入れることができます。

public static string Serialize<T>(T item, bool includeHeader = false)
{
    MemoryStream memStream = new MemoryStream();
    using (XmlTextWriter textWriter = new XmlTextWriter(memStream, Encoding.Unicode))
    {
        System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(T));
        serializer.Serialize(textWriter, item);

        memStream = textWriter.BaseStream as MemoryStream;
    }
    if (memStream != null)
        if (includeHeader)
        {
            return @"<?xml version=""1.0"" encoding=""utf-8"" ?>" + Environment.NewLine + Encoding.Unicode.GetString(memStream.ToArray());
        }
        else
        {
            return Encoding.Unicode.GetString(memStream.ToArray());
        }
    else
        return null;
}
0
competent_tech