I have a object which has a string property that has a value with double quotes in it. I need to serialize this object and then use that XML. I wont be deserializing this xml.
I am having trouble getting the right content in the XML file. Let me explain with a code sample:
[Serializable]
public class Test {
[XmlElement]
public string obj { get; set; }
}
class Program {
static void Main(string[] args) {
var st ="Priority == \"1\"";
Test test = new Test();
test.obj = st;
//Serialize this object
XmlSerializer xsSubmit = new XmlSerializer(typeof(Test));
StringWriter sww = new StringWriter();
XmlWriter writer = XmlWriter.Create(sww, new XmlWriterSettings {
OmitXmlDeclaration = true
});
var ns = new XmlSerializerNamespaces();//just to make things simpler here
ns.Add(string.Empty, string.Empty);
xsSubmit.Serialize(writer, test, ns);
//My XML
var xml = sww.ToString();
}
}
I need my xml to be:
<Test><obj>Priority=="1"</obj></Test>
I now get:
<Test><obj>Priority==\"1\"</obj></Test>
I even tried to encode the string into HTML using var html = HttpUtility.HtmlEncode(st);
In this case, the varible html is in the right format however on serializing I get:
<Test><obj>Priority=="1"</obj></Test>
Need some help please.
There was no issue with the code.
I actually get<Test><obj>Priority=="1"</obj></Test> and this is fine. The mistake I was making was that I was reading the value on the debugger. When I write it somewhere, the content was in the correct format.
The " didnt get converted to " because double quotes are as such accepted in an XML document. I can work with that in this case!
Related
I'm working on a C# program and I'm trying to serialise XML.
I have the following tag:
using System.Xml.Serialization;
...
[XmlElement("MV")]
public MultiVerse MultiVerse { get; set; }
When I don't fill in this value, the tag <MV> is not present, but I would like to get a tag <MV/> in that case:
Currently I have <HM><ID>Some_ID</ID></HM>.
I'd like to have <HM><ID>Some_ID</ID><MV/></HM>.
I already tried preceeding the line with [Required] but that didn't work, and I think that filling in the IsNullable attribute is the good approach.
Edit1, after some investigation on the internet
On the internet, there are quite some advises on modifying the XmlWriter but in my project, the whole serialisation is done as follows:
public override string ToString()
{
...
using (var stream = new StringWriter())
using (var writer = XmlWriter.Create(stream, settings))
{
var serializer = new XmlSerializer(base.GetType());
serializer.Serialize(writer, this, ns);
return stream.ToString();
}
...
}
As you can see, this is so general that I prefer not to do any modifications in here, hence I'm looking for a way to customise the [XmlElement] directive.
Edit2: XmlWriter settings:
The XmlWriter settings look as follows:
// Remove Declaration
var settings = new XmlWriterSettings
{
Indent = false,
OmitXmlDeclaration = true,
NewLineHandling = NewLineHandling.None,
NewLineOnAttributes = false,
};
Does anybody have an idea?
Thanks in advance
There is https://learn.microsoft.com/en-us/dotnet/api/system.xml.serialization.xmlelementattribute.isnullable?view=net-6.0 so e.g.
[XmlElement("MV", IsNullable=true)]
public MultiVerse MultiVerse { get; set; }
would give you, for a null value, a serialization as <MV xsi:nil="true" /> (or possibly <MV xsi:nil="true"></MV> as ensuring the short tag notation is not something the standard writers give you control over but my experience is that .NET usually uses it for empty elements so you might be lucky that your wanted serialization format is the default one .NET outputs).
This is the way I'm currently solving my issue (don't laugh):
public override string ToString()
{
string temp = base.ToString();
temp = temp.Replace(" p2:nil=\"true\"", "");
temp = temp.Replace(" xmlns:p2=\"http://www.w3.org/2001/XMLSchema-instance\"", "");
temp = temp.Replace("MV />", "MV/>");
return temp;
}
This is hidious! Does anybody have a better solution?
Thanks
I need to change the behaviour of XmlWriter for my project to change the way that empty xml elements are serialised. Currently, my code uses XmlWriter and XmlSerializer like so:
public string Serialize(object o)
{
XmlWriterSettings settings = new XmlWriterSettings();
...
StringWriter stringWriter = new StringWriter();
XmlWriter xmlWriter = XmlWriter.Create(stringWriter, settings);
XmlSerializer serializer = new XmlSerializer(o.GetType());
serializer.Serialize(xmlWriter, o);
return stringWriter.ToString();
}
When serializing my xml, empty elements are being serialized to <emptyElement/>, but I need the xml to serialize empty elements to <emptyElement></emptyElement>. The best solution I've found for this was stated in this a Microsoft forum years ago: https://social.msdn.microsoft.com/Forums/en-US/979315cf-6727-4979-a554-316218ab8b24/xml-serialize-empty-elements?forum=xmlandnetfx
The faster and safer way of doing this is by writing your own subclass of the XmlWriter and give it to XmlSerializer.
YourXmlWriter would aggregate standard one and would translate all WriteEndElement() calls to WriteFullEndElement() calls.
I've tried writing my own subclass of XmlWriter, overriding the two methods I need to override:
public abstract class CustomXmlWriter : XmlWriter
{
public override void WriteEndElement()
{
WriteFullEndElement();
}
public override Task WriteEndElementAsync()
{
return WriteFullEndElementAsync();
}
}
In theory, I believe this should work. However, when trying to use the code, I'm coming up against a brick wall around XmlWriter.Create. I cannot cast the resulting XmlWriter to my CustomXmlWriter for obvious reasons, and I can't override the method as it's a static method.
How am I meant to deal with the static Create method? The only other way I can think of doing this is to scrap the idea of my own CustomXmlWriter, and to simply manipulate the string at the end of my method, but this feels very wrong. I don't know if what I'm trying to achieve is possible, or if there is a simple setting somewhere that I cannot seem to find anywhere.
Try following Regex as a temporary fix. The variable input can be the entire xml string and it will replace every occurrence. :
static void Main(string[] args)
{
string input = "<emptyElement/>";
string patternNullTag = #"\<(?'tagname'\w+)/\>";
string output = Regex.Replace(input, patternNullTag, ReplaceNullElement);
}
static string ReplaceNullElement(Match match)
{
string tagname = match.Value.Replace("<", "").Replace("/>", "");
string newElement = "<" + tagname + ">" + "</" + tagname + ">";
return newElement;
}
I am running into the problem where I can't write serialized objects to a specific location in an XML document.
If I have an XML file that looks like:
<?xml version="1.0" encoding="utf-8"?>
<Spellements>
<MapSave />
</Spellements>
And I want to load my list of objects into the MapSave tag. How do I do it?
(the List of object type Node is passed in)
XmlSerializer ser = new XmlSerializer(typeof(List<Node>));
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
settings.NewLineOnAttributes = true;
string fileName = Application.dataPath + "/Saves/Save" + GameVariables.saveFile + ".xml";
using (XmlWriter writer = XmlWriter.Create(File.Open(fileName, FileMode.Open), settings))
{
ser.Serialize(writer, list);
}
I have no problem serializing all the objects and writing them to the file. It works fine, but I seem to have no way to write it specifically as elements inside the area.
I have to use an XML writer to do Serialization, but there is no way to set the XML writer's "start writing here" location. As of now it just overwrites the whole document.
(I've done more then my fair share of research, I know how to set the writing location when using an XmlDocument, but not with XmlWriter -- Help please.
If you don't mind everytime writing the whole file you can do something like this:
[Serializable]
public class Spellement
{
public Spellement() { }
List<Node> mapSave = new List<Node>();
public List<Node> MapSave
{
get { return mapSave; }
set { mapSave = value; }
}
}
[Serializable]
public class Node
{
public Node() { }
public string Text { get; set; }
}
XmlSerializer xs = new XmlSerializer(typeof(Spellement));
using (XmlWriter st = XmlWriter.Create("C:\\test.xml"))
{
xs.Serialize(st, s);
}
I'm using XmlSerializer to serialize and then deserialize a simple object. When I deserialize the object to my surprise I find a child object was not properly deserialized but instead turned into XmlNode[].
Here is very nearly the structure I've got:
// This line I put in here as a way of sneaking into the XML the
// root node's C# namespace, since it's not the same as the
// deserializing code and the deserializing code seemed unable to
// deserialize properly without knowing the Type (see my code below).
// So I basically just use this fake construct to get the namespace
// and make a Type of it to feed the XmlSerializer() instantiation.
[XmlRoot(Namespace = "http://foo.com/CSharpNamespace/Foo.Bar")]
// This is because QueuedFile can be given to the Argument array.
[XmlInclude(typeof(QueuedFile))]
// This class is Foo.Bar.CommandAndArguments
public class CommandAndArguments {
public String Command;
public object[] Arguments;
}
// I don't think this matters to XmlSerialize, but just in case...
[Serializable()]
// I added this line just thinking maybe it would help, but it doesn't
// do anything. I tried it without the XmlType first, and that
// didn't work.
[XmlType("Foo.Baz.Bat.QueuedFile")]
// This class is Foo.Baz.Bat.QueuedFile (in a different c#
// namespace than CommandAndArguments and the deserializing code)
public QueuedFile {
public String FileName;
public String DirectoryName;
}
And the code which deserializes it looks like:
public static object DeserializeXml(String objectToDeserialize)
{
String rootNodeName = "";
String rootNodeNamespace = "";
using (XmlReader xmlReader = XmlReader.Create(new StringReader(objectToDeserialize)))
{
if (xmlReader.MoveToContent() == XmlNodeType.Element)
{
rootNodeName = xmlReader.Name;
rootNodeNamespace = xmlReader.NamespaceURI;
if (rootNodeNamespace.StartsWith("http://foo.com/CSharpNamespace/"))
{
rootNodeName = rootNodeNamespace.Substring("http://foo.com/CSharpNamespace/".Length) + "." +
rootNodeName;
}
}
}
//MessageBox.Show(rootNodeName);
try
{
Type t = DetermineTypeFromName(rootNodeName);
if (t == null)
{
throw new Exception("Could not determine type of serialized string. Type listed as: "+rootNodeName);
}
var s = new XmlSerializer(t);
return s.Deserialize(new StringReader(objectToDeserialize));
// object o = new object();
// MethodInfo castMethod = o.GetType().GetMethod("Cast").MakeGenericMethod(t);
// return castMethod.Invoke(null, new object[] { s.Deserialize(new StringReader(objectToDeserialize)) });
}
catch (InvalidOperationException)
{
return null;
}
}
And here is the XML when the CommandAndArguments is serialized:
<?xml version="1.0" encoding="utf-16"?>
<CommandAndArguments xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://foo.com/CSharpNamespace/Foo.Bar">
<Command>I am a command</Command>
<Arguments>
<anyType xsi:type="Foo.Baz.Bat.QueuedFile">
<FileName xmlns="">HelloWorld.txt</FileName>
<DirectoryName xmlns="">C:\foo\bar</DirectoryName>
</anyType>
</Arguments>
</CommandAndArguments>
But when I deserialize I am given a CommandAndArguments object where Arguments is XmlNode[] with the first item being the attribute giving the QueuedFile as the type and the other indices being elements of the properties. But why wasn't the QueuedFile object recreated?
I suspect this might somehow have do with C# namespaces and the engine doing the deserializing not being able to find or work with QueuedFile... But I don't see why since when I forgot the XmlInclude() it made sure to tell me it didn't expect QueuedFile and now that I've added the XmlInclude() I get no error, just an incomplete deserialization.
Help? I've read everything I can find to read and Googled everything I know to Google and am stuck. I certainly have a lot to learn about XML serialization but I'm not sure how I'm failing at something which should be pretty simple (I actually did something almost exactly like this before without any problem, the only difference then was that everything was in the same C# namespace).
Are you able to change the XML format or is it fixed? I don't know what the problem you are having is, but I use the DataContractSerializer classes extensively with no problems.
http://msdn.microsoft.com/en-us/library/system.runtime.serialization.datacontractserializer.aspx
public static void WriteObject(string fileName)
{
Console.WriteLine(
"Creating a Person object and serializing it.");
Person p1 = new Person("Zighetti", "Barbara", 101);
FileStream writer = new FileStream(fileName, FileMode.Create);
DataContractSerializer ser =
new DataContractSerializer(typeof(Person));
ser.WriteObject(writer, p1);
writer.Close();
}
public static void ReadObject(string fileName)
{
Console.WriteLine("Deserializing an instance of the object.");
FileStream fs = new FileStream(fileName,
FileMode.Open);
XmlDictionaryReader reader =
XmlDictionaryReader.CreateTextReader(fs, new XmlDictionaryReaderQuotas());
DataContractSerializer ser = new DataContractSerializer(typeof(Person));
// Deserialize the data and read it from the instance.
Person deserializedPerson =
(Person)ser.ReadObject(reader, true);
reader.Close();
fs.Close();
Console.WriteLine(String.Format("{0} {1}, ID: {2}",
deserializedPerson.FirstName, deserializedPerson.LastName,
deserializedPerson.ID));
}
To anyone coming along with a similar problem, depending on your situation you're probably better off with NetDataContractSerializer. It is an alternative to DataContractSerializer which records the .Net types in the XML making deserialization a breeze, since it knows exactly what types are involved and thus you do not need to tell it what type the root object is with the deserialize command. And it can produce output in XML or binary form (I prefer XML for easier debugging).
Here is some sample code for easily serializing and deserializing an object to and from a string:
private static object Deserialize(string xml)
{
object toReturn = null;
using (Stream stream = new MemoryStream())
{
byte[] data = System.Text.Encoding.UTF8.GetBytes(xml);
stream.Write(data, 0, data.Length);
stream.Position = 0;
var netDataContractSerializer = new NetDataContractSerializer();
toReturn = netDataContractSerializer.Deserialize(stream);
}
return toReturn;
}
private static string Serialize(object obj)
{
using (var memoryStream = new MemoryStream())
using (var reader = new StreamReader(memoryStream))
{
var netDataContractSerializer = new NetDataContractSerializer();
netDataContractSerializer.Serialize(memoryStream, obj);
memoryStream.Position = 0;
return reader.ReadToEnd();
}
}
Easy as pie!
I have noticed that XmlWriter.WriteRaw appears to not work properly (it escapes xml characters) when the writer is created using XElement.CreateWriter. The below test case reproduces the problem. Is my usage incorrect? Does anyone know how to achieve the desired behavior? I need to be able to write a raw xml string to an XmlWriter and incorporate that xml into an XElement.
[Test]
public void XElementWriterTest()
{
var xelement = new XElement("test");
using (var writer = xelement.CreateWriter())
{
writer.WriteRaw(#"<some raw='xml' />");
}
Assert.That(xelement.ToString(), Is.EqualTo(#"<test><some raw='xml' /></test>"));
// actual : "<test><some raw='xml' /></test>"
}
Is XElement.Parse() an option for you at all?
[TestMethod]
public void XElementWriterTest()
{
var xelement = new XElement("test");
const string newXML = #"<some raw='xml' />";
var child = XElement.Parse(newXML);
xelement.Add(child);
Assert.AreEqual(xelement.ToString(SaveOptions.DisableFormatting), #"<test><some raw=""xml"" /></test>");
}