.NET XML 직렬화 문제가 있습니까? [닫은]
C # XML 직렬화를 수행 할 때 공유 할 것이라고 생각한 몇 가지 문제에 봉착했습니다.
- KeyValuePairs와 같이 읽기 전용 인 항목은 직렬화 할 수 없습니다.
- 일반 사전을 직렬화 할 수 없습니다. 대신이 래퍼 클래스를 사용해보십시오 ( http://weblogs.asp.net/pwelter34/archive/2006/05/03/444961.aspx ).
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;
[XmlRoot("dictionary")]
public class SerializableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, IXmlSerializable
{
public System.Xml.Schema.XmlSchema GetSchema()
{
return null;
}
public void ReadXml(System.Xml.XmlReader reader)
{
XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));
XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue));
bool wasEmpty = reader.IsEmptyElement;
reader.Read();
if (wasEmpty)
return;
while (reader.NodeType != System.Xml.XmlNodeType.EndElement)
{
reader.ReadStartElement("item");
reader.ReadStartElement("key");
TKey key = (TKey)keySerializer.Deserialize(reader);
reader.ReadEndElement();
reader.ReadStartElement("value");
TValue value = (TValue)valueSerializer.Deserialize(reader);
reader.ReadEndElement();
this.Add(key, value);
reader.ReadEndElement();
reader.MoveToContent();
}
reader.ReadEndElement();
}
public void WriteXml(System.Xml.XmlWriter writer)
{
XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));
XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue));
foreach (TKey key in this.Keys)
{
writer.WriteStartElement("item");
writer.WriteStartElement("key");
keySerializer.Serialize(writer, key);
writer.WriteEndElement();
writer.WriteStartElement("value");
TValue value = this[key];
valueSerializer.Serialize(writer, value);
writer.WriteEndElement();
writer.WriteEndElement();
}
}
}
다른 XML 직렬화가 있습니까?
또 다른 큰 문제는 웹 페이지 (ASP.NET)를 통해 XML을 출력 할 때 Unicode Byte-Order Mark 를 포함하고 싶지 않다는 것 입니다. 물론 BOM을 사용하거나 사용하지 않는 방법은 거의 동일합니다.
불량 (BOM 포함) :
XmlTextWriter wr = new XmlTextWriter(stream, new System.Text.Encoding.UTF8);
좋은:
XmlTextWriter wr = new XmlTextWriter(stream, new System.Text.UTF8Encoding(false))
BOM을 원하지 않음을 나타 내기 위해 명시 적으로 false를 전달할 수 있습니다. 사이의 명확하고 분명한 차이점에 주목 Encoding.UTF8
와 UTF8Encoding
.
The three extra BOM Bytes at the beginning are (0xEFBBBF) or (239 187 191).
Reference: http://chrislaco.com/blog/troubleshooting-common-problems-with-the-xmlserializer/
I can't make comments yet, so I will comment on Dr8k's post and make another observation. Private variables that are exposed as public getter/setter properties, and do get serialized/deserialized as such through those properties. We did it at my old job al the time.
One thing to note though is that if you have any logic in those properties, the logic is run, so sometimes, the order of serialization actually matters. The members are implicitly ordered by how they are ordered in the code, but there are no guarantees, especially when you are inheriting another object. Explicitly ordering them is a pain in the rear.
I've been burnt by this in the past.
When serializing into an XML string from a memory stream, be sure to use MemoryStream#ToArray() instead of MemoryStream#GetBuffer() or you will end up with junk characters that won't deserialize properly (because of the extra buffer allocated).
http://msdn.microsoft.com/en-us/library/system.io.memorystream.getbuffer(VS.80).aspx
If the serializer encounters a member/property that has an interface as its type, it won't serialize. For example, the following won't serialize to XML:
public class ValuePair
{
public ICompareable Value1 { get; set; }
public ICompareable Value2 { get; set; }
}
Though this will serialize:
public class ValuePair
{
public object Value1 { get; set; }
public object Value2 { get; set; }
}
IEnumerables<T>
that are generated via yield returns are not serializable. This is because the compiler generates a separate class to implement yield return and that class is not marked as serializable.
You can't serialize read-only properties. You must have a getter and a setter, even if you never intend to use deserialization to turn XML into an object.
For the same reason, you can't serialize properties that return interfaces: the deserializer wouldn't know what concrete class to instantiate.
Oh here's a good one: since the XML serialization code is generated and placed in a separate DLL, you don't get any meaningful error when there is a mistake in your code that breaks the serializer. Just something like "unable to locate s3d3fsdf.dll". Nice.
Can't serialize an object which doesn't have a parameterless construtor (just got bitten by that one).
And for some reason, from the following properties, Value gets serialised, but not FullName:
public string FullName { get; set; }
public double Value { get; set; }
I never got round to working out why, I just changed Value to internal...
One more thing to note: you can't serialize private/protected class members if you are using the "default" XML serialization.
But you can specify custom XML serialization logic implementing IXmlSerializable in your class and serialize any private fields you need/want.
http://msdn.microsoft.com/en-us/library/system.xml.serialization.ixmlserializable.aspx
If your XML Serialization generated assembly is not in the same Load context as the code attempting to use it, you will run into awesome errors like:
System.InvalidOperationException: There was an error generating the XML document.
---System.InvalidCastException: Unable to cast object
of type 'MyNamespace.Settings' to type 'MyNamespace.Settings'. at
Microsoft.Xml.Serialization.GeneratedAssembly.
XmlSerializationWriterSettings.Write3_Settings(Object o)
The cause of this for me was a plugin loaded using LoadFrom context which has many disadvantages to using the Load context. Quite a bit of fun tracking that one down.
You may face problems serializing objects of type Color and/or Font.
Here are the advices, that helped me:
http://www.codeproject.com/KB/XML/xmlsettings.aspx
http://www.codeproject.com/KB/cs/GenericXmlSerializition.aspx
See "Advanced XML Schema Definition Language Attributes Binding Support" for details of what is supported by the XML Serializer, and for details on the way in which the supported XSD features are supported.
If you try to serialize an array, List<T>
, or IEnumerable<T>
which contains instances of subclasses of T
, you need to use the XmlArrayItemAttribute to list all the subtypes being used. Otherwise you will get an unhelpful System.InvalidOperationException
at runtime when you serialize.
Here is part of a full example from the documentation
public class Group
{
/* The XmlArrayItemAttribute allows the XmlSerializer to insert both the base
type (Employee) and derived type (Manager) into serialized arrays. */
[XmlArrayItem(typeof(Manager)), XmlArrayItem(typeof(Employee))]
public Employee[] Employees;
Private variables/properties are not serialized in the default mechanism for XML serialization, but are in binary serialization.
Properties marked with the Obsolete
attribute aren't serialized. I haven't tested with Deprecated
attribute but I assume it would act the same way.
I can't really explain this one, but I found this won't serialise:
[XmlElement("item")]
public myClass[] item
{
get { return this.privateList.ToArray(); }
}
but this will:
[XmlElement("item")]
public List<myClass> item
{
get { return this.privateList; }
}
And also worth noting that if you're serialising to a memstream, you might want to seek to 0 before you use it.
Be careful serialising types without explicit serialisation, it can result in delays while .Net builds them. I discovered this recently while serialising RSAParameters.
If your XSD makes use of substitution groups, then chances are you can't (de)serialize it automatically. You'll need to write your own serializers to handle this scenario.
Eg.
<xs:complexType name="MessageType" abstract="true">
<xs:attributeGroup ref="commonMessageAttributes"/>
</xs:complexType>
<xs:element name="Message" type="MessageType"/>
<xs:element name="Envelope">
<xs:complexType mixed="false">
<xs:complexContent mixed="false">
<xs:element ref="Message" minOccurs="0" maxOccurs="unbounded"/>
</xs:complexContent>
</xs:complexType>
</xs:element>
<xs:element name="ExampleMessageA" substitutionGroup="Message">
<xs:complexType mixed="false">
<xs:complexContent mixed="false">
<xs:attribute name="messageCode"/>
</xs:complexContent>
</xs:complexType>
</xs:element>
<xs:element name="ExampleMessageB" substitutionGroup="Message">
<xs:complexType mixed="false">
<xs:complexContent mixed="false">
<xs:attribute name="messageCode"/>
</xs:complexContent>
</xs:complexType>
</xs:element>
In this example, an Envelope can contain Messages. However, the .NET's default serializer doesn't distinguish between Message, ExampleMessageA and ExampleMessageB. It will only serialize to and from the base Message class.
Private variables/properties are not serialized in XML serialization, but are in binary serialization.
I believe this also gets you if you are exposing the private members through public properties - the private members don't get serialised so the public members are all referencing null values.
참고URL : https://stackoverflow.com/questions/67959/net-xml-serialization-gotchas
'Programming' 카테고리의 다른 글
지연된 객체 란 무엇입니까? (0) | 2020.07.14 |
---|---|
Visual Studio, 여러 스레드 중 하나를 디버그 (0) | 2020.07.14 |
요청 데이터를 잃지 않고 ASP.NET MVC에서 ToAction을 리디렉션하는 방법 (0) | 2020.07.14 |
기업의 Git 기반 소스 제어 : 제안 된 툴 및 사례? (0) | 2020.07.14 |
Git은 서브 모듈에 대한 커밋의 SHA1을 어디에 저장합니까? (0) | 2020.07.14 |