I have an issue where trying to serialize an object containing a string array to soap causes an exception in my application. I am doing the following to create the soap formatter:
XmlTypeMapping mapping = new SoapReflectionImporter().ImportTypeMapping(obj.GetType());
XmlSerializer serializer = new XmlSerializer(mapping);
when I call Serialize on the serializer I get the following exception. "Token StartElement in state Epilog would result in an invalid XML document."
However if I just want regular xml and create my XmlSerializer like this:
XmlSerializer serializer = new XmlSerializer(obj.GetType());
Everything works fine and the xml contains the string array.
I have a full example below that reproduces the issue on my machine if someone could take a look I would be very grateful as I am out of ideas!
static void Main(string[] args)
{
GetAlarmEventTypesResponse bob = new GetAlarmEventTypesResponse();
bob.GetAlarmEventTypesTypes = new string[] { "bob", "bob1", "bob2" };
bob.version = "2.0";
// works
string xml = GetRegularDocument(bob);
Console.WriteLine(xml);
// throws exception
string soap = GetSoapDocument(bob);
Console.WriteLine(soap);
}
//------------------------------------------------------------------------------
[System.Xml.Serialization.SoapTypeAttribute(Namespace = "http://example/common/dataexchange/2011/05")]
public class GetAlarmEventTypesResponse
{
public GetAlarmEventTypesResponse()
{
version = "1.2";
}
[System.Xml.Serialization.XmlArrayItemAttribute("Type", IsNullable = false)]
public string[] GetAlarmEventTypesTypes { get; set; }
[System.Xml.Serialization.XmlAttributeAttribute()]
public string version { get; set; }
}
//------------------------------------------------------------------------------
public static string GetRegularDocument(object obj)
{
string document = null;
XmlSerializer serializer = new XmlSerializer(obj.GetType());
using (StringWriter textWriter = new StringWriter())
{
serializer.Serialize(textWriter, obj);
document = textWriter.ToString();
}
return document;
}
//------------------------------------------------------------------------------
public static string GetSoapDocument(object obj)
{
string document = null;
XmlTypeMapping mapping = new SoapReflectionImporter().ImportTypeMapping(obj.GetType());
XmlSerializer serializer = new XmlSerializer(mapping);
using (StringWriter textWriter = new StringWriter())
{
serializer.Serialize(textWriter, obj);
document = textWriter.ToString();
}
return document;
}
Related
I have followed a good tutorial that shows how to make an Automation Framework in C# Selenium.
The config file is in XML at the moment, but I wanted some more practice and change it to a .json file.
At the moment we are using the namespace System.Xml.XPath; and my question is, are there a similar for JSON? Lets say "System.Json;" that works the same as my XML reader. So I don't need to refactor to much code, or is it unavoidably?
This is how it works at the moment.
//Initialize
ConfigReader.SetFrameworkSettings();
public class ConfigReader
{
public static void SetFrameworkSettings()
{
XPathItem aut;
string strFilename = Environment.CurrentDirectory.ToString() + "\\Config\\GlobalConfig.xml";
FileStream stream = new FileStream(strFilename, FileMode.Open);
XPathDocument document = new XPathDocument(stream);
XPathNavigator navigator = document.CreateNavigator();
//Get XML Details and pass it in XPathItem type variables
aut = navigator.SelectSingleNode("AutoFramework/RunSettings/AUT");
Settings.AUT = aut.Value.ToString();
}
}
public class Settings
{
public static string AUT { get; set; }
}
Would be awesome if you could just change this two lines
XPathDocument document = new XPathDocument(stream);
XPathNavigator navigator = document.CreateNavigator()
And the XpathItem
Cheers
I would recommend using Newtonsoft.Json which is available from Nuget.
To "reuse" your current code you would have to make some basic SettingsConverter first:
public static class SettingsConverter
{
public static string FromXmlToJson(string xml)
{
Settings settings = null;
// read your xml file and assign values to the settings object
// now you can "serialize" settings object into Json
return JsonSerialization.Serialize(settings);
}
public static string FromJsonToXml(string json)
{
Settings settings = JsonSerialization.Deserialize<MeSettings>(json);
// save settings using your "xml" serialization
return xmlString; // return xml string
}
}
To use these methods I'm using this JsonSerialization helper class :
public static class JsonSerialiation
{
public static string Serialize<T>(T obj)
{
using (MemoryStream stream = new MemoryStream())
{
using (JsonTextWriter writer = new JsonTextWriter(new StreamWriter(stream))
{
JsonSerializer serializer = new JsonSerializer();
serializer.Serializer(writer, obj);
writer.Flush();
stream.Position = 0;
using (StreamReader reader = new StreamREader(stream))
{
return reader.ReadToEnd();
}
}
}
}
public static T Deserialize<T>(string jsonString)
{
using (JsonTextReader reader = new JsonTextReader(new StringReader(str)))
{
JsonSerializer serializer = new JsonSerializer();
return serializer.Deserialize<T>(reader)
}
}
}
Above example requires from you to change your Settings class from static :
public class Settings
{
public static string AUT { get; set; }
}
To instance :
public class Settings
{
public string AUT { get; set; }
}
If you prefer to keep it as static. You should use JObject from Newtonsoft.Json library :
JObject obj = JObject.Parse(jsonString);
Settings.AUT = obj.SelectToken("AUT").Value<string>();
You can always use JsonConvert.Serialize<T>() and JsonConvert.Deserialize<T>() methods instead of the JsonSerialization helper class that I've made but in my opinion the less control you have over your code the bigger the problems will be.
I'm posting XML to an API which has been serialised from a C# class at my end, everything looks correct apart from where a particular namespace is when the XML is generated but I am not sure why.
I need the a namespace to be be on the appointmentAvailability element. Ignore the escape backslashes that's just because I copied the xml out of the visual studio debugger.
Where am I going wrong?
function to test
var a = new AppointmentAvailability
{
TimeSlot = "AM"
};
var aa = new AppointmentAvailabilityContainer();
aa.appointmentAvailability = a;
var nd = new Dictionary<string, string>();
nd.Add("a", "http://maindomain/Appointments");
var x = XmlManager.SerializeThis<AppointmentAvailabilityContainer>(aa,nd);
var checkappointmentavailability = FluidWeb.CheckAppointmentAvailability(a);
generic serialise function
public static string SerializeThis<T>(object obj, Dictionary<string, string> dictionary)
{
try
{
XmlDocument xd = new XmlDocument();
string xml = "";
var xs = new XmlSerializer(obj.GetType());
XmlSerializerNamespaces xsn = new XmlSerializerNamespaces();
foreach (var item in dictionary)
{
xsn.Add(item.Key, item.Value);
}
using (MemoryStream ms = new MemoryStream())
{
xs.Serialize(ms, obj, xsn);
ms.Position = 0;
xd.Load(ms);
xml = xd.InnerXml;
}
return xml;
}
catch(XmlException x)
{
var xml = "Could not serialise.";
return xml;
}
}
classes
[XmlRoot("AppointmentAvailability", Namespace = "http://maindomain")]
public class AppointmentAvailabilityContainer
{
[XmlElement("appointmentAvailability")]
public AppointmentAvailability appointmentAvailability { get; set; }
}
[XmlRoot("appointmentAvailability", Namespace = "http://maindomain/Appointments")]
[XmlType("a")]
public class AppointmentAvailability
{
[XmlElement("TimeSlot")]
public string TimeSlot { get; set; }
}
xml generated
<?xml version=\"1.0\"?>
<AppointmentAvailability xmlns:a=\"http://maindomain/Appointments\" xmlns=\"http://maindomain">
<appointmentAvailability>
<a:TimeSlot>AM</a:TimeSlot>
</appointmentAvailability>
</AppointmentAvailability>
xml trying to achieve
<?xml version=\"1.0\"?>
<AppointmentAvailability xmlns=\"http://maindomain">
<appointmentAvailability xmlns:a=\"http://maindomain/Appointments\">
<a:TimeSlot>AM</a:TimeSlot>
</appointmentAvailability>
</AppointmentAvailability>
Guys i am Having trouble with Advance OOP and design patterns also in Xmlserialization
this would be my code:
public void Send(Message message, string recipient)
{
XmlSerializer ser = new XmlSerializer(typeof(XmlNode));
XmlWriter writer = new XmlWriter(Messenger.outbox + message.Recipient);
ser.Serialize(writer, message);
writer.Close();
}
You can't instantiate an abstract class, you are looking for XmlWriter.Create method.
Creates a new XmlWriter instance using the specified filename.
You can't directly create a new XmlWriter, instead you use the XmlWriter static class to create a writer instance.
Given a class Message:
public class Message
{
public string Outbox { get; set; }
public string Recipient { get; set; }
}
You could serialize it like this:
XmlSerializer ser = new XmlSerializer(typeof(Message));
using (StringWriter sww = new StringWriter())
{
using (XmlWriter writer = XmlWriter.Create(sww))
{
ser.Serialize(writer, new Message() { Outbox = "onething", Recipient = "another thing" });
var xml = sww.ToString();
}
}
Can some one help for a strange Serialization issue which I getting in a one environment and it working fine in all environments..but it failed a particular environment..so I specified as strange here
Code
public SomeType[] Deserialize(string xml)
{
XmlSerializer serializer = new XmlSerializer(typeof(SomeType[]));
StringReader stringReader = new StringReader(xml);
SomeType[] types = (SomeType[])serializer.Deserialize(stringReader);
stringReader.Close();
return types;
}
Serialized XML data:
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfSomeType>
<SomeType>
<Field1>val</Field1>
<Field2>val</Field2>
<Field3>val</Field3>
</SomeType>
</ArrayOfSomeType>
And the exception is:
System.InvalidCastException: Unable to cast object of type 'SomeType[]' to type 'SomeType'.
at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriterSomeType.Write11_SomeType(Object o)
The error here is in the Serialize method that you don't show. Your Deserialize method works without error. The following Serialize method works fine:
static string Serialize(SomeType[] values)
{
using (var sw = new StringWriter())
{
var serializer = new XmlSerializer(typeof(SomeType[]));
serializer.Serialize(sw, values);
return sw.ToString();
}
}
If I had to guess, you have the following:
var serializer = new XmlSerializer(typeof(SomeType));
If you want the exact same output without unnecessary namespace alias declarations, change the method to include:
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("","");
serializer.Serialize(sw, values, ns);
Edit showing the current code working fine:
using System;
using System.IO;
using System.Xml.Serialization;
public class SomeType
{
public string Field1 { get; set; }
public string Field2 { get; set; }
public string Field3 { get; set; }
}
public class MyTest
{
public static SomeType[] Deserialize(string xml)
{
XmlSerializer serializer = new XmlSerializer(typeof(SomeType[]));
StringReader stringReader = new StringReader(xml);
SomeType[] types = (SomeType[])serializer.Deserialize(stringReader);
stringReader.Close();
return types;
}
public static void Main()
{
string xml = #"<?xml version=""1.0"" encoding=""utf-8""?>
<ArrayOfSomeType>
<SomeType>
<Field1>val</Field1>
<Field2>val</Field2>
<Field3>val</Field3>
</SomeType>
</ArrayOfSomeType>";
var items = Deserialize(xml);
foreach (var item in items)
{
Console.WriteLine("{0}, {1}, {2}",
item.Field1, item.Field2, item.Field3);
}
}
}
This example uses a StringWriter to hold the serialized data, then calling ToString() gives the actual string value:
Person john = new Person();
XmlSerializer xmlSerializer = new XmlSerializer(typeof(Person));
StringWriter stringWriter = new StringWriter();
xmlSerializer.Serialize(stringWriter, john);
string serializedXML = stringWriter.ToString();
Is there any easier/Cleaner way to do this? All of the Serialize() overloads seem to use a Stream or Writer.
UPDATE: Asked a similar question about serializing an IEnumerable via an Extension Method .
Fun with extension methods...
var ret = john.ToXmlString()
public static class XmlTools
{
public static string ToXmlString<T>(this T input)
{
using (var writer = new StringWriter())
{
input.ToXml(writer);
return writer.ToString();
}
}
public static void ToXml<T>(this T objectToSerialize, Stream stream)
{
new XmlSerializer(typeof(T)).Serialize(stream, objectToSerialize);
}
public static void ToXml<T>(this T objectToSerialize, StringWriter writer)
{
new XmlSerializer(typeof(T)).Serialize(writer, objectToSerialize);
}
}
More or less your same solution, just using an extension method:
static class XmlExtensions {
// serialize an object to an XML string
public static string ToXml(this object obj) {
// remove the default namespaces
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add(string.Empty, string.Empty);
// serialize to string
XmlSerializer xs = new XmlSerializer(obj.GetType());
StringWriter sw = new StringWriter();
xs.Serialize(sw, obj, ns);
return sw.GetStringBuilder().ToString();
}
}
[XmlType("Element")]
public class Element {
[XmlAttribute("name")]
public string name;
}
class Program {
static void Main(string[] args) {
Element el = new Element();
el.name = "test";
Console.WriteLine(el.ToXml());
}
}
I created this helper method, but I haven't tested it yet. Updated the code per orsogufo's comments (twice):
private string ConvertObjectToXml(object objectToSerialize)
{
XmlSerializer xmlSerializer = new XmlSerializer(objectToSerialize.GetType());
StringWriter stringWriter = new StringWriter();
xmlSerializer.Serialize(stringWriter, objectToSerialize);
return stringWriter.ToString();
}
Seems like no body actually answered his question, which is no, there is no way to generate an XML string without using a stream or writer object.