开发者

How to keep XmlSerializer from killing NewLines in Strings?

Suppose I have a simple Class with just one Member a String.

public class Abc
{
    private String text;

    public String Text
    {
        get { return this.text; }
        set { this.text = 开发者_如何学Cvalue; }
    }
}

Now when I serialize and then deserialize it with the questionable XmlSerializer any text containing newlines ('\r\n' or Environment.NewLine) are transformed to '\n'.

How do I keep the newlines?


It is not the XmlSerializer but the XmlWriter which is removing your CR. To retain it we must have the writer convert CR to its character entity 
.

XmlWriterSettings ws = new XmlWriterSettings();
ws.NewLineHandling = NewLineHandling.Entitize;

XmlSerializer ser = new XmlSerializer( typeof( Abc ) );
using (XmlWriter wr = XmlWriter.Create( "abc.xml", ws )) {
    ser.Serialize( wr, s );
}

This is exactly the same with DataContractSerializer:

var ser = new DataContractSerializer( typeof( Abc ) );
using (XmlWriter wr = XmlWriter.Create( "abc.xml", ws )) {
    ser.Serialize( wr, s );
}

Why do we need to do this?

This is because compliant XML parsers must, before parsing, translate CRLF and any CR not followed by a LF to a single LF. This behavior is defined in the End-of-Line handling section of the XML 1.0 specification.

As this happens before parsing, you need to encode CR as its character entity if you want the CR to exist in the document.


public class SerializeAny<TF> where TF : new()
{
    public static TF Deserialize(string serializedData)
    {
        try
        {
            var xmlSerializer = new XmlSerializer(typeof(TF));
            TF collection;
            using (var xmlReader = new XmlTextReader(serializedData, XmlNodeType.Document, null))
            {
                collection = (TF)xmlSerializer.Deserialize(xmlReader);
            }
            return collection;
        }
        catch (Exception)
        {


        }

        return new TF();
    }


    public static TF DeserializeZip(string path)
    {
        try
        {
            var bytes = File.ReadAllBytes(path);

            string serializedData = Unzip(bytes);

            TF collection = Deserialize(serializedData);


            return collection;
        }
        catch (Exception)
        {


        }

        return new TF();
    }

    public static string Serialize(TF options)
    {
        var xml = "";

        try
        {
            var xmlSerializer = new XmlSerializer(typeof(TF));
            using (var stringWriter = new StringWriter())
            {
                xmlSerializer.Serialize(stringWriter, options);
                xml = stringWriter.ToString();
            }
        }
        catch (Exception ex)
        {

            return ex.Message;
        }



        return xml;
    }

    public static string SerializeZip(TF options, string path)
    {
        var xml = "";

        try
        {
            xml = Serialize(options);
            var zip = Zip(xml);
            File.WriteAllBytes(path, zip);
        }
        catch (Exception ex)
        {

            return ex.Message;
        }



        return xml;
    }



    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times")]
    internal static String SerializeObject<T>(T obj, Encoding enc)
    {
        using (var ms = new MemoryStream())
        {
            var xmlWriterSettings = new System.Xml.XmlWriterSettings()
            {
                // If set to true XmlWriter would close MemoryStream automatically and using would then do double dispose
                // Code analysis does not understand that. That's why there is a suppress message.
                CloseOutput = false,
                Encoding = enc,
                OmitXmlDeclaration = false,
                Indent = true
            };
            using (var xw = XmlWriter.Create(ms, xmlWriterSettings))
            {
                var s = new XmlSerializer(typeof(T));
                s.Serialize(xw, obj);
            }

            return enc.GetString(ms.ToArray());
        }
    }

    private static void CopyTo(Stream src, Stream dest)
    {
        byte[] bytes = new byte[4096];

        int cnt;

        while ((cnt = src.Read(bytes, 0, bytes.Length)) != 0)
        {
            dest.Write(bytes, 0, cnt);
        }
    }

    private static byte[] Zip(string str)
    {
        var bytes = Encoding.UTF8.GetBytes(str);

        using (var msi = new MemoryStream(bytes))
        using (var mso = new MemoryStream())
        {
            using (var gs = new GZipStream(mso, CompressionMode.Compress))
            {
                //msi.CopyTo(gs);
                CopyTo(msi, gs);
            }

            return mso.ToArray();
        }
    }

    private static string Unzip(byte[] bytes)
    {
        using (var msi = new MemoryStream(bytes))
        using (var mso = new MemoryStream())
        {
            using (var gs = new GZipStream(msi, CompressionMode.Decompress))
            {
                CopyTo(gs, mso);
            }

            return Encoding.UTF8.GetString(mso.ToArray());
        }
    }

}


public class BinarySerialize where T : new() { public static string Serialize(T options, string path) {

            var xml = "";
            try
            {
                File.Delete(path);
            }
            catch (Exception)
            {


            }

            try
            {
                using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
                {
                    var bf = new BinaryFormatter();


                    bf.Serialize(fs, options);
                }


            }
            catch (Exception ex)
            {

                return ex.Message;
            }



            return xml;





        }

        public static T Deserialize(string path)
        {
            T filteroptions;
            using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                var bf = new BinaryFormatter();
                filteroptions = (T)bf.Deserialize(fs);
            }
            return filteroptions;

        }
    }


Use this code:

public static FilterOptions Deserialize(string serializedData)
{
    try
    {
        var xmlSerializer = new XmlSerializer(typeof(FilterOptions));
        var xmlReader = new XmlTextReader(serializedData,XmlNodeType.Document,null);
        var collection = (FilterOptions)xmlSerializer.Deserialize(xmlReader);
        return collection;
    }
    catch (Exception)
    {


    }

    return new FilterOptions();
}


Nice solution, Lachlan Roche!

The function below (in VB.NET) uses a StringWriter to return a String, rather than writing the result to a file using an XmlWriter.

  ''' <summary>
  ''' Exports the object data to an XML formatted string.
  ''' Maintains CR characters after deserialization.
  ''' The object must be serializable to work.
  ''' </summary>

  Public Function ExportObjectXml(ByVal obj As Object) As String
    If obj Is Nothing Then
      Return String.Empty
    End If

    Dim serializer As New XmlSerializer(obj.GetType)
    Dim settings As New XmlWriterSettings With {.NewLineHandling = NewLineHandling.Entitize}

    Using output As New StringWriter
      Using writer As XmlWriter = XmlWriter.Create(output, settings)
        serializer.Serialize(writer, obj)
        Return output.ToString
      End Using
    End Using
  End Function
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜