I searched and tried some attributes but none of them worked for my below scenario:
public class ObjSer
{
[XmlElement("Name")]
public string Name
{
get; set;
}
}
//Code to serialize
var obj = new ObjSer();
obj.Name = "<tag1>Value</tag1>";
var stringwriter = new System.IO.StringWriter();
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(obj.GetType());
serializer.Serialize(stringwriter, obj);
Output would be as follows:
<ObjSer><Name><tag1>Value</tag1></Name></ObjSer>
But I need output as:
<ObjSer><Name><tag1>Value</tag1></Name></ObjSer>
Scenario 2: In some cases I need to set:
obj.Name = "Value";
Is there any attribute or method I can override to make it possible?
You can't with default serializer. XmlSerializer does encoding of all values during serialization.
If you want your member to hold xml value, it must be XmlElement. Here is how you can accomplish it
public class ObjSer
{
[XmlElement("Name")]
public XmlElement Name
{
get; set;
}
}
var obj = new ObjSer();
// <-- load xml
var doc = new XmlDocument();
doc.LoadXml("<tag1>Value</tag1>");
obj.Name = doc.DocumentElement;
// --> assign the element
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(obj.GetType());
serializer.Serialize(Console.Out, obj);
Output:
<?xml version="1.0" encoding="IBM437"?>
<ObjSer xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Name>
<tag1>Value</tag1>
</Name>
</ObjSer>
UPDATE:
In case if you want to use XElement instead of XmlElement, here is sample on how to do it
public class ObjSer
{
[XmlElement("Name")]
public XElement Name
{
get; set;
}
}
static void Main(string[] args)
{
//Code to serialize
var obj = new ObjSer();
obj.Name = XDocument.Parse("<tag1>Value</tag1>").Document.FirstNode as XElement;
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(obj.GetType());
serializer.Serialize(Console.Out, obj);
}
No, you can't. It is the natural and usual behaviour of the xml serializer. The serializer doesn't have to handle within the XML strings. So, it escapes the xml as expected.
You should decode the escaped string again while deserializing it.
If you want to add dynamically elements in the XML I suggest you to use Linq to XML and you can create tag1 or another kind of elements easily.
I would suggest serializing to an XDocument then converting that to a string and manually unescaping the desired part and writing it to a file. I would say this would give you the least headache it shouldn't be more than a couple lines of code. If you need it I can provide some code example if needed.
I found one more way of changing the type
public class NameNode
{
public string tag1
{
get; set;
}
[XmlText]
public string Value
{
get; set;
}
}
public class ObjSer
{
[XmlElement("Name")]
public NameNode Name
{
get; set;
}
}
To set value of Name:
var obj = new ObjSer();
var valueToSet = "<tag1>Value</tag1>";
//or var valueToSet = "Value";
//With XML tag:
if (valueToSet.Contains("</"))
{
var doc = new XmlDocument();
doc.LoadXml(valueToSet);
obj.Name.tag1 = doc.InnerText;
}
else //Without XML Tags
{
obj.Name.Value = senderRecipient.Name;
}
This solution will work in both cases, but has limitation. It will work only for predefined tags (ex. tag1)
Related
When I serialize the value : If there is no value present in for data then it's coming like below format.
<Note>
<Type>Acknowledged by PPS</Type>
<Data />
</Note>
But what I want xml data in below format:
<Note>
<Type>Acknowledged by PPS</Type>
<Data></Data>
</Note>
Code For this i have written :
[Serializable]
public class Notes
{
[XmlElement("Type")]
public string typeName { get; set; }
[XmlElement("Data")]
public string dataValue { get; set; }
}
I am not able to figure out what to do for achieve data in below format if data has n't assign any value.
<Note>
<Type>Acknowledged by PPS</Type>
<Data></Data>
</Note>
You can do this by creating your own XmlTextWriter to pass into the serialization process.
public class MyXmlTextWriter : XmlTextWriter
{
public MyXmlTextWriter(Stream stream) : base(stream, Encoding.UTF8)
{
}
public override void WriteEndElement()
{
base.WriteFullEndElement();
}
}
You can test the result using:
class Program
{
static void Main(string[] args)
{
using (var stream = new MemoryStream())
{
var serializer = new XmlSerializer(typeof(Notes));
var writer = new MyXmlTextWriter(stream);
serializer.Serialize(writer, new Notes() { typeName = "Acknowledged by PPS", dataValue="" });
var result = Encoding.UTF8.GetString(stream.ToArray());
Console.WriteLine(result);
}
Console.ReadKey();
}
If you saved your string somewhere (e.g a file) you can use this simple Regex.Replace:
var replaced = Regex.Replace(File.ReadAllText(name), #"<([^<>/]+)\/>", (m) => $"<{m.Groups[1].Value.Trim()}></{m.Groups[1].Value.Trim()}>");
File.WriteAllText(name, replaced);
IMO it's not possibe to generate your desired XML using Serialization. But, you can use LINQ to XML to generate the desired schema like this -
XDocument xDocument = new XDocument();
XElement rootNode = new XElement(typeof(Notes).Name);
foreach (var property in typeof(Notes).GetProperties())
{
if (property.GetValue(a, null) == null)
{
property.SetValue(a, string.Empty, null);
}
XElement childNode = new XElement(property.Name, property.GetValue(a, null));
rootNode.Add(childNode);
}
xDocument.Add(rootNode);
XmlWriterSettings xws = new XmlWriterSettings() { Indent=true };
using (XmlWriter writer = XmlWriter.Create("D:\\Sample.xml", xws))
{
xDocument.Save(writer);
}
Main catch is in case your value is null, you should set it to empty string. It will force the closing tag to be generated. In case value is null closing tag is not created.
Kludge time - see Generate System.Xml.XmlDocument.OuterXml() output thats valid in HTML
Basically after XML doc has been generated go through each node, adding an empty text node if no children
// Call with
addSpaceToEmptyNodes(xmlDoc.FirstChild);
private void addSpaceToEmptyNodes(XmlNode node)
{
if (node.HasChildNodes)
{
foreach (XmlNode child in node.ChildNodes)
addSpaceToEmptyNodes(child);
}
else
node.AppendChild(node.OwnerDocument.CreateTextNode(""))
}
(Yes I know you shouldn't have to do this - but if your sending the XML to some other system that you can't easily fix then have to be pragmatic about things)
You can add a dummy field to prevent the self-closing element.
[XmlText]
public string datavalue= " ";
Or if you want the code for your class then Your class should be like this.
public class Notes
{
[XmlElement("Type")]
public string typeName { get; set; }
[XmlElement("Data")]
private string _dataValue;
public string dataValue {
get {
if(string.IsNullOrEmpty(_dataValue))
return " ";
else
return _dataValue;
}
set {
_dataValue = value;
}
}
}
In principal, armen.shimoon's answer worked for me. But if you want your XML output pretty printed without having to use XmlWriterSettings and an additional Stream object (as stated in the comments), you can simply set the Formatting in the constructor of your XmlTextWriter class.
public MyXmlTextWriter(string filename) : base(filename, Encoding.UTF8)
{
this.Formatting = Formatting.Indented;
}
(Would have posted this as a comment but am not allowed yet ;-))
Effectively the same as Ryan's solution which uses the standard XmlWriter (i.e. there's no need for a derived XmlTextWriter class), but written using linq to xml (XDocument)..
private static void AssignEmptyElements(this XNode node)
{
if (node is XElement e)
{
e.Nodes().ToList().ForEach(AssignEmptyElements);
if (e.IsEmpty)
e.Value = string.Empty;
}
}
usage..
AssignEmptyElements(document.FirstNode);
I have a class that could be serialized on its own, for example:
[XmlRoot("NameOfMyRoot", Namespace = "myNamespace")]
public class Inner
{
public Inner(){}
public string SomeString { get; set; }
}
Which is perfectly serializing into (I am using ns alias for myNamespace, see full demo)
<?xml version="1.0" encoding="utf-16"?>
<NameOfMyRoot xmlns:ns="myNamespace">
<ns:SomeString></ns:SomeString>
</NameOfMyRoot>
Now I want this object to be part of another one (the wrapper):
[XmlRoot("Root")]
public class Outer<T>
{
public T Property{ get; set; }
public Outer(){}
public Outer(T inner)
{
Property = inner;
}
}
Which gives me this:
<?xml version="1.0" encoding="utf-16"?>
<Root xmlns:ns="myNamespace">
<Property>
<ns:SomeString></ns:SomeString>
</Property>
</Root>
What I want is just embedding inner object as-is into its parent, like so:
<?xml version="1.0" encoding="utf-16"?>
<Root>
<NameOfMyRoot xmlns:ns="myNamespace">
<ns:SomeString></ns:SomeString>
</NameOfMyRoot>
</Root>
Notice that namespace should not move to root, and I can't specify element's name, since there will be many different types.
Fiddle with full example.
Of cource, I can just serialize them separately and combine through some nasty string manipulation, but I hope there is a neat way to achieve this somehow.
There's no easy way to do that. You can do it at runtime, though:
// perhaps discover these details at runtime
var attribs = new XmlAttributeOverrides();
attribs.Add(typeof(Outer<Inner>), "Property", new XmlAttributes
{
XmlElements = { new XmlElementAttribute {ElementName = "NameOfMyRoot" } }
});
attribs.Add(typeof(Inner), "SomeString", new XmlAttributes
{
XmlElements = { new XmlElementAttribute { Namespace = "myNamespace"} }
});
var ser = new XmlSerializer(typeof(Outer<Inner>), attribs);
var obj = new Outer<Inner> { Property = new Inner { SomeString = "abc" } };
ser.Serialize(Console.Out, obj);
Note that you must store and re-use such a serializer; for simple new XmlSerializer() examples, the serializer is cached internally and re-used; however, for non-trivial cases like this, a new dynamic type is emitted each time, so you will leak memory (it won't be unloaded).
That gives you:
<Root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<NameOfMyRoot>
<SomeString xmlns="myNamespace">abc</SomeString>
</NameOfMyRoot>
</Root>
If you don't want the two xmlns alias declaration, those can be removed separately, but they do not change the meaning, at least to a compliant reader. Likewise, the ns can be added as an alias:
var obj = new Outer<Inner> { Property = new Inner { SomeString = "abc" } };
var ns = new XmlSerializerNamespaces();
ns.Add("", "");
ns.Add("ns", "myNamespace");
ser.Serialize(Console.Out, obj, ns);
which gives:
<Root xmlns:ns="myNamespace">
<NameOfMyRoot>
<ns:SomeString>abc</ns:SomeString>
</NameOfMyRoot>
</Root>
As an alternative approach:
using System;
using System.Xml;
using System.Xml.Serialization;
[XmlRoot("NameOfMyRoot")]
public class Inner
{
public Inner() { }
[XmlElement(Namespace = "myNamespace")]
public string SomeString { get; set; }
}
static class Program
{
static void Main()
{
using (XmlWriter writer = XmlWriter.Create(Console.Out))
{
writer.WriteStartDocument(false);
writer.WriteStartElement("Root");
var ser = new XmlSerializer(typeof(Inner));
var obj = new Inner { SomeString = "abc" };
var ns = new XmlSerializerNamespaces();
ns.Add("", "");
ns.Add("ns", "myNamespace");
ser.Serialize(writer, obj, ns);
writer.WriteEndElement();
writer.WriteEndDocument();
}
}
}
What I've done here is to manually write the root node, and only get the XmlSerializer to write the inner content. Note that I had to change the attributes aroung SomeString to make it work in the way you expect (where it is the string that has the namespace, not the object).
There are various types, in a special case which can be configured in different ways. How to serialize them?
[Serializable]
[XmlRoot("RootXml", Namespace = "")]
public class RootXml
{
object _schemaVersion;
[XmlElement("SchemaVersion")]
public object SchemaVersion
{
get { return _schemaVersion; }
set { _schemaVersion = value; }
}
List<object> _test;
[XmlElement("Test")]
public List<object> Test
{
get { return _test; }
set { _test = value; }
}
public RootXml()
{
}
}
I.e. root can include different objects, and they have to be serialized...
I have a xml-format approximately of such
look:
<?xml version="1.0" encoding="windows-1251"?>
<RootXml>
<SchemaVersion Number="" />
<Report Code="">
<Period Code="" Date="">
<Source ClassCode="" Code="">
<Form Code="">
<Column Num="1" Name="" />
<Column Num="2" Name="" />
<Column Num="3" Name="" />
<Document>
<Data code="11" />
<Data code="12">
<Px Num="1" Value="1" />
<Px Num="2" Value="1" />
<Px Num="4" Value="2" />
<Px Num="5" Value="2" />
</Data>
<Data code="13" />
</Document>
</Form>
</Source>
</Period>
</Report>
</RootXml>
In which some elements can change a little (Document, Document with tags, Document with the status, etc.),
included in others (for example, report incl. in scheme) ... and do not know how to change in the future.
I want to construct a set of "formats" which will also have various components, to be substituted...
Maybe for this purpose you shouldn't use serialization, and to define
set of attributes, and a reflection to process objects and to form xml (approximately just as XmlSerializer)???
You are trying to serialize and deserialize data with polymorphic fields. You have a few options here:
If you know in advance all possible types that might be encountered in a polymorphic field, you can use attributes to tell XmlSerializer how to serialize and deserialize each type. In particular, for a polymorphic field, apply [XmlElement("DerivedElementName", Type = typeof(DerivedElementType))] for every derived type that might be encountered.
For instance, simplifying your RootXml class somewhat, the following allows for two different types of report to be serialized:
[XmlRoot("Report", Namespace = "")]
public class Report
{
[XmlAttribute]
public string Code { get; set; }
[XmlElement]
public decimal TotalCost { get; set; }
}
[XmlRoot("DifferentReport", Namespace = "fuuuu")]
public class DifferentReport
{
public DifferentReport() { }
public DifferentReport(string code, string value)
{
this.Code = code;
this.Value = value;
}
[XmlAttribute]
public string Code { get; set; }
[XmlText]
public string Value { get; set; }
}
[XmlRoot("RootXml", Namespace = "")]
public class RootXml
{
public RootXml() { }
object _test;
[XmlElement("Report", Type=typeof(Report))]
[XmlElement("DifferentReport", Type = typeof(DifferentReport))]
public object Data
{
get { return _test; }
set { _test = value; }
}
}
And then later, both of the following can be serialized and deserialized:
var root1 = new RootXml { Data = new Report { Code = "a code", TotalCost = (decimal)101.01 } };
var root2 = new RootXml { Data = new DifferentReport { Code = "a different code", Value = "This is the value of the report" } };
Note that you can use the same technique with polymorphic lists, in which case the serializer will expect sequences of elements with the specified names:
[XmlRoot("RootXml", Namespace = "")]
public class RootXml
{
public RootXml() { }
List<object> _test;
[XmlElement("Report", Type=typeof(Report))]
[XmlElement("DifferentReport", Type = typeof(DifferentReport))]
public List<object> Data
{
get { return _test; }
set { _test = value; }
}
}
If the XML could be anything and you don't know what it might contain (because you must deserialize XML from the future versions and reserialize it without data loss, for example) you may need to load your XML into an XDocument then manually search for data using Linq-to-XML. For information on how to do this, see here: Basic Queries (LINQ to XML).
You could adopt a hybrid approach where you load the XML into an XDocument, then deserialize and serialize familiar portions with XmlSerializer, using the following extension methods:
public static class XObjectExtensions
{
public static T Deserialize<T>(this XContainer element)
{
return element.Deserialize<T>(new XmlSerializer(typeof(T)));
}
public static T Deserialize<T>(this XContainer element, XmlSerializer serializer)
{
using (var reader = element.CreateReader())
{
object result = serializer.Deserialize(reader);
if (result is T)
return (T)result;
}
return default(T);
}
public static XElement Serialize<T>(this T obj, bool omitStandardNamespaces = true)
{
return obj.Serialize(new XmlSerializer(obj.GetType()), omitStandardNamespaces);
}
public static XElement Serialize<T>(this T obj, XmlSerializer serializer, bool omitStandardNamespaces = true)
{
var doc = new XDocument();
using (var writer = doc.CreateWriter())
{
XmlSerializerNamespaces ns = null;
if (omitStandardNamespaces)
(ns = new XmlSerializerNamespaces()).Add("", ""); // Disable the xmlns:xsi and xmlns:xsd lines.
serializer.Serialize(writer, obj, ns);
}
return doc.Root;
}
}
Then use them to pick out and deserialize known portions of your XML as follows:
var doc = XDocument.Parse(xml);
var reportElement = doc.Root.Element("Report");
if (reportElement != null)
{
var report1 = doc.Root.Element("Report").Deserialize<Report>();
// Do something with the report.
// Create a different report
var differentReport = new DifferentReport { Code = report1.Code + " some more code", Value = "This is the value of the report" };
var differentElement = differentReport.Serialize();
reportElement.AddAfterSelf(differentElement);
reportElement.Remove();
}
OK, given that you are using c# 2.0, you can load your Xml into an XmlDocument and use it as described here: Process XML Data Using the DOM Model. This is a precursor API to Linq-to-XML and is somewhat harder to work with -- but nevertheless totally functional.
You can also adopt the hybrid approach and use XmlSerializer to deserialize and re-serialize known chunks of an XmlDocument. Here are some extension methods for this purpose -- but since you're using c# 2.0, you must remove the this keyword:
public static class XmlNodeExtensions
{
public static XmlElement SerializeToXmlElement<T>(this T o, XmlElement parent)
{
return SerializeToXmlElement(o, parent, new XmlSerializer(o.GetType()));
}
public static XmlElement SerializeToXmlElement<T>(this T o, XmlElement parent, XmlSerializer serializer)
{
int oldCount = parent.ChildNodes.Count;
XPathNavigator navigator = parent.CreateNavigator();
using (XmlWriter writer = navigator.AppendChild())
{
writer.WriteComment(""); // Kludge suggested here: https://social.msdn.microsoft.com/Forums/en-US/9ff20a3c-913d-4c6f-a18a-c10040290862/how-to-xmlserialize-directly-into-xmldocument?forum=asmxandxml
serializer.Serialize(writer, o);
}
XmlElement returnedElement = null;
for (int i = parent.ChildNodes.Count - 1; i >= oldCount; i--)
{
XmlComment comment = parent.ChildNodes[i] as XmlComment;
if (comment != null)
{
parent.RemoveChild(comment);
}
else
{
returnedElement = (parent.ChildNodes[i] as XmlElement) ?? returnedElement;
}
}
return returnedElement;
}
public static XmlDocument SerializeToXmlDocument<T>(this T o)
{
return SerializeToXmlDocument(o, new XmlSerializer(o.GetType()));
}
public static XmlDocument SerializeToXmlDocument<T>(this T o, XmlSerializer serializer)
{
XmlDocument doc = new XmlDocument();
using (XmlWriter writer = doc.CreateNavigator().AppendChild())
serializer.Serialize(writer, o);
return doc;
}
public static T Deserialize<T>(this XmlElement element)
{
return Deserialize<T>(element, new XmlSerializer(typeof(T)));
}
public static T Deserialize<T>(this XmlElement element, XmlSerializer serializer)
{
using (var reader = new XmlNodeReader(element))
return (T)serializer.Deserialize(reader);
}
}
Given those methods, you can do things like:
// Load the document from XML
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
// Find all nodes with name "Report"
foreach (XmlElement reportNode in doc.SelectNodes("/RootXml/Report"))
{
// Deserialize as a Report
Report report = XmlNodeExtensions.Deserialize<Report>(reportNode);
// Do something with it
// Create a new Report, based on the original report.
DifferentReport differentReport = new DifferentReport(report.Code + " some more code", "This is the value of the report"); ;
// Add the new report to the children of RootXml
XmlElement newNode = XmlNodeExtensions.SerializeToXmlElement(differentReport, (XmlElement)reportNode.ParentNode);
}
As you can see this is quite similar to what is possible with Linq-to-XML.
I'm trying to use YAXLib to serialize an object. If I serialize directly to a string it works fine, but if I try to serialize to an XmlWriter I get an empty xml.
This is a sample class declaration (nothing weird, right?):
public class City
{
public string Name { get; set; }
public long Population { get; set; }
}
This is what I'm doing to serialize it:
/* Object to serialize */
var city = new City() { Name = "Montevideo", Population = 1500000 };
var serializer = new YAXSerializer(typeof(City));
/* Serialize to XmlWriter */
var stringWriter = new StringWriter();
var xmlWriter = XmlWriter.Create(stringWriter);
serializer.Serialize(city, xmlWriter);
var result1 = stringWriter.ToString(); // result1 is ""
/* Serialize to String */
var result2 = serializer.Serialize(city); // result2 is "<City>...</City>"
I need to use the XmlWriter approach because I want to control several aspects of the resulting xml through XmlWriterSettings (omit xml declaration, avoid indentation, control new line handling, ...).
Anyone had successfully serialized to XmlWriter using YAXLib? What am I doing wrong?
A call to xmlWriter.Flush() may solve your problem, as it did on my own testing:
serializer.Serialize(city, xmlWriter);
xmlWriter.Flush();
I have the following XML structure (edited for brevity) which I have no control over.
<GetVehicles>
<ApplicationArea>
<Sender>
<Blah></Blah>
</Sender>
</ApplicationArea>
<DataArea>
<Error>
<Blah></Blah>
</Error>
<Vehicles>
<Vehicle>
<Colour>Blue</Colour>
<NumOfDoors>3</NumOfDoors>
<BodyStyle>Hatchback</BodyStyle>
<Vehicle>
</Vehicles>
</DataArea>
</GetVehicles>
I have the following Class:
[XmlRoot("GetVehicles"), XmlType("Vehicle")]
public class Vehicle
{
public string Colour { get; set; }
public string NumOfDoors { get; set; }
public string BodyStyle { get; set; }
}
I want to be able to deserialize the XML into a single instance of this Vehicle class. 99% of the time, the XML should only return a single 'Vehicle' element. I'm not yet dealing with it yet if it contains multiple 'Vehicle' elements inside the 'Vehicles' element.
Unfortunately, the XML data isn't currently being mapped to my class properties; they are being left blank upon calling my Deserialize method.
For completeness, here is my Deserialize method:
private static T Deserialize<T>(string data) where T : class, new()
{
if (string.IsNullOrEmpty(data))
return null;
var ser = new XmlSerializer(typeof(T));
using (var sr = new StringReader(data))
{
return (T)ser.Deserialize(sr);
}
}
I don't care about the other more parent elements such as 'ApplicationArea', 'Error' etc. I am only interesting in extracting the data within the 'Vehicle' element. How do I get it to only deserialize this data from the XML?
Building on Ilya's answer:
This can be optimized slightly, since there is already a loaded node: there is no need to pass the xml down to a string (using vehicle.ToString()), only to cause the serializer to have to re-parse it (using a StringReader).
Instead, we can created a reader directly using XNode.CreateReader:
private static T Deserialize<T>(XNode data) where T : class, new()
{
if (data == null)
return null;
var ser = new XmlSerializer(typeof(T));
return (T)ser.Deserialize(data.CreateReader());
}
Or if data is a XmlNode, use a XmlNodeReader:
private static T Deserialize<T>(XmlNode data) where T : class, new()
{
if (data == null)
return null;
var ser = new XmlSerializer(typeof(T));
using (var xmlNodeReader = new XmlNodeReader(data))
{
return (T)ser.Deserialize(xmlNodeReader);
}
}
We can then use:
var vehicle = XDocument.Parse(xml)
.Descendants("Vehicle")
.First();
Vehicle v = Deserialize<Vehicle>(vehicle);
I'm not aware of the full context of your problem, so this solution might not fit into your domain. But one solution is to define your model as:
[XmlRoot("Vehicle")] //<-- optional
public class Vehicle
{
public string Colour { get; set; }
public string NumOfDoors { get; set; }
public string BodyStyle { get; set; }
}
and pass specific node into Deserialize method using LINQ to XML:
var vehicle = XDocument.Parse(xml)
.Descendants("Vehicle")
.First();
Vehicle v = Deserialize<Vehicle>(vehicle.ToString());
//display contents of v
Console.WriteLine(v.BodyStyle); //prints Hatchback
Console.WriteLine(v.Colour); //prints Blue
Console.WriteLine(v.NumOfDoors); //prints 3