Использование .Net 3.0 и VS2005.
Рассматриваемые объекты потребляются из службы WCF, а затем сериализуются обратно в XML для устаревшего API. Таким образом, вместо сериализации TestObject, он сериализовал .TestObject, в котором отсутствовал атрибут [XmlRoot]; однако все атрибуты [Xml *] для дочерних элементов были в сгенерированном коде прокси, поэтому они работали нормально. Таким образом, все дочерние элементы работали нормально, но не включающий элемент, потому что атрибут [XmlRoot] не был включен в сгенерированный код прокси. Исходный объект, включающий атрибут [XmlRoot], отлично сериализуется вручную.
Могу ли я включить в прокси-код атрибут [XmlRoot], чтобы сгенерированный прокси-класс также правильно сериализовался? Если я не могу этого сделать, я подозреваю, что мне придется использовать [XmlType], но это вызовет незначительный ущерб, требующий от меня изменения других компонентов, поэтому я бы предпочел первый. Я также хочу избежать необходимости вручную редактировать автоматически созданный прокси-класс.
Вот пример кода (я включил клиент и службу в одно приложение, потому что это быстро и для тестовых целей. Закомментируйте код ссылки службы и добавьте ссылку на службу при запуске приложения, затем раскомментируйте код службы и запустите .)
namespace SerializationTest {
class Program {
static void Main( string[] args ) {
Type serviceType = typeof( TestService );
using (ServiceHost host = new ServiceHost(
serviceType,
new Uri[] {
new Uri( "http://localhost:8080/" )
}
))
{
ServiceMetadataBehavior behaviour = new ServiceMetadataBehavior();
behaviour.HttpGetEnabled = true;
host.Description.Behaviors.Add( behaviour );
host.AddServiceEndpoint( serviceType, new BasicHttpBinding(), "TestService" );
host.AddServiceEndpoint( typeof( IMetadataExchange ), new BasicHttpBinding(), "MEX" );
host.Open();
TestServiceClient client = new TestServiceClient();
localhost.TestObject to = client.GetObject();
String XmlizedString = null;
using (MemoryStream memoryStream = new MemoryStream()) {
XmlSerializer xs = new XmlSerializer( typeof( localhost.TestObject ) );
using (XmlWriter xmlWriter = XmlWriter.Create(memoryStream)) {
xs.Serialize( xmlWriter, to );
memoryStream = (MemoryStream)xmlWriter.BaseStream;
XmlizedString = Encoding.UTF8.GetString( memoryStream.ToArray() );
Console.WriteLine( XmlizedString );
}
}
}
Console.ReadKey();
}
}
[Serializable]
[XmlRoot( "SomethingElse" )]
public class TestObject {
private bool _worked;
public TestObject() { Worked = true; }
[XmlAttribute( AttributeName = "AttributeWorked" )]
public bool Worked {
get { return _worked; }
set { _worked = value; }
}
}
[ServiceContract]
public class TestService {
[OperationContract]
[XmlSerializerFormat]
public TestObject GetObject() {
return new TestObject();
}
}
}
Вот генерируемый XML-код.
<?xml version = "1.0" encoding = "utf-8"?>
<TestObject xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd = "http://www.w3.org/2001/XMLSchema" AttributeWorked = "true" />
Я не удивлен, что XmlRoot не создается. Что мне кажется странным, так это то, что сгенерированный класс не называется SomethingElse. Вы сгенерировали прокси после добавления атрибута XmlRoot?
Я считаю, что нужно избегать изменения схемы / сгенерированного прокси. Просто используйте их для получения, затем развернитесь и снова отправьте на сторону с наследием. Но было бы неплохо не писать новый набор определений - отсюда желание переименовывать на лету. Я предложил использовать XmlAttributeOverrides.





Я нашел человека, который предоставляет средства для решения этой ситуации:
Используя этот подход XmlAttributeOverrides, я написал следующее:
private static XmlSerializer GetOverridedSerializer()
{
// set overrides for TestObject element
XmlAttributes attrsTestObject = new XmlAttributes();
XmlRootAttribute rootTestObject = new XmlRootAttribute("SomethingElse");
attrsTestObject.XmlRoot = rootTestObject;
// create overrider
XmlAttributeOverrides xOver = new XmlAttributeOverrides();
xOver.Add(typeof(localhost.TestObject), attrsTestObject);
XmlSerializer xSer = new XmlSerializer(typeof(localhost.TestObject), xOver);
return xSer;
}
Просто поместите этот метод в класс Program вашего примера и замените следующую строку в Main():
//XmlSerializer xs = new XmlSerializer(typeof(localhost.TestObject));
XmlSerializer xs = GetOverridedSerializer();
А потом бегите смотреть результаты.
Вот что у меня получилось:
<?xml version = "1.0" encoding = "utf-8"?><SomethingElse xmlns:xsi = "http://www.w3.o
rg/2001/XMLSchema-instance" xmlns:xsd = "http://www.w3.org/2001/XMLSchema" Attribu
teWorked = "true" />
Я думаю, проблема не в использовании XmlSerializer, а в коде, созданном для прокси. В реальном сценарии сериализацией XML будет заниматься WCF, поэтому вы не можете напрямую влиять на используемый XmlSerializer.
Верно. Но суть в том, чтобы иметь возможность перепрофилировать контракт данных, не изменяя его физически (регенерируя или редактируя вручную) и, таким образом, не мешая первоначальному намерению использовать его с WCF.
Извините, вы правы, я упустил суть. Существует также несколько более простое решение, использующее конструктор XmlSerializer, который принимает параметр XmlRootAttribute.
Для моей проблемы эти переопределения были бы слишком распространены, поэтому я не думаю, что это решение именно то, что мне нужно. Я также думаю, что когда мы перейдем к 2008 году и начнем делиться типами, это уже не будет иметь значения. Поэтому, хотя я не использую это, я отмечу это как ответ.
== ЕСЛИ ==
Это только для атрибута XmlRoot. XmlSerializer имеет один конструктор, в котором вы можете указать атрибут XmlRoot.
Престижность csgero за указание на нее. Его комментарий должен стать решением.
XmlSerializer Constructor (Type, XmlRootAttribute)
Initializes a new instance of the
XmlSerializerclass that can serialize objects of the specified type into XML documents, and deserialize an XML document into object of the specified type. It also specifies the class to use as the XML root element.
Плохой конструктор. Созданная сборка сериализатора Xml не кэшируется.
Вы контролируете схему xsd, которая используется для создания прокси?