XML Serialization different elementName for same object - c#

I have the following Model in ASP.Net Core
[Serializable]
[XmlRoot(ElementName = "CREDIT")]
public class Credit
{
[XmlElement(ElementName = "D_numbern")]
public string Number get; set; }
}
I did the serialization with StringWriter, the problem I should get XML like that
<CREDITS>
<CREDIT ID="1">
<D_number1>06</D_number1>
</CREDIT>
<CREDIT ID="2">
<D_number2>06</D_number2>
</CREDIT>
</CREDITS>
I didn't find a solution how to make n dynamic for each credit .
thanks in advance for any assistance.

What you're after isn't something that XmlSerializer supports, and frankly it is a bad design for xml generally; not only is it redundant (xml is ordered: there's no need to tell it that you're item 1/2/3), but it is actively hostile to most xml tooling, including serializers, schema validators, etc.
My strong suggestion is to rethink the xml you want, or challenge the requirements if it isn't your idea. If the D_number42 will always match the ID="42" that is the parent, frankly the suffix serves absolutely no purpose. If it is a different number that only looks the same in these examples by coincidence, then: <D_number someattribute="42">06</D_number>
But if you must do this, you'll have to do it manually, via XDocument, XmlDocument, or XmlWriter.
as an example using XmlWriter:
static void WriteCredit(XmlWriter xml, string id, string number)
{
xml.WriteStartElement("CREDITS");
xml.WriteStartElement("CREDIT");
xml.WriteAttributeString("ID", id);
xml.WriteElementString("D_number" + id, number);
xml.WriteEndElement();
xml.WriteEndElement();
}
usage that writes to the console:
using (var xml = XmlWriter.Create(Console.Out))
{
WriteCredit(xml, "1", "06");
}

Related

XML attributes are ignored upon serialization

I am having a weird issue I am banging my head against...
I have a class like this:
[XmlRoot("DoesntWork")]
class Root
{
[XmlElement(ElementName="WontWork", Order=1)]
public string xmlOutPropertyName
{...}
}
and I am serializing with this:
textBox1.Clear();
Root rt = new Root();
rt.xmlOutPropertyName = "[0000000001]";
XmlSerializer serializer = new XmlSerializer();
textBox1.Text = serializer.Serialize(rt);
but I always get xml that returns the names of class and property and not the name I want.
<Root>
<xmlOutPropertyName>[0000000001]</xmlOutPropertyName>
</Root>
Any idea why this is happening??
Dumb mistake, I was not paying attention and using the wrong serialization library.

XML Deserialization of many types

I am deserializing a large xml doc into a C# object.
I've run into an issue where there are multiple xml elements on the same line, and am having trouble re-constructing them properly in code.
A snippet example as so:
<parent>
<ce:para view="all">
Text <ce:cross-ref refid="123">[1]</ce:cross-ref> More Text <ce:italic>Italicized text</ce:italic> and more text here
</ce:para>
<ce:para>...</ce:para>
</parent>
The generated C# class looks like this
[XmlRoot(ElementName = "para", Namespace = "namespace")]
public class Para
{
[XmlElement(ElementName = "cross-ref", Namespace = "namespace")]
public List<Crossref> Crossref { get; set; }
[XmlText]
public List<string> Text { get; set; }
[XmlElement(ElementName = "italic", Namespace = "namespace")]
public List<Italic> Italic { get; set; }
}
I want to be able to loop over this object and re-construct the sentence as a plain string.
Text [1] More Text Italicized Text and more text here
The only problem is though when the deserialization happens, the order is lost as each bit is stuck into it's respective object. This means I have no way of knowing how to reconstruct the string back to how it is supposed to be.
Text: {"Text", "More Text", "and more text here"}
Crossref: {"[1]"}
Italic: {"Italicized Text"}
I've thought about bringing in the whole element in as a string, and then scrubbing the tags out of it, but I'm not sure how to properly get it deserialized. Or if there is a better way to go about it.
Disclaimer: I am not able to alter the XML document as it is coming in from a 3rd party.
Thanks
Once you have deserialized the 3rd party XML into an object that directly matches the XML's schema (as you have done already in your example above) you should be able to use XmlNode.InnerText() on the <ce:para node to extract what you're looking for without having to write any parsing code.
At that point, you could do a translation from the object you deserialized into from the raw 3rd party XML into an object which flattens out the <ce:para node into a simple string.
As per Chris' request, I'm posting my solution. It probably could used refining as I'm not very experienced with linq queries.
XDocument xdoc = xmlAdapter.GetAsXDoc(xmlstring);
IEnumerable<XElement> body = from b in xdoc.Descendants()
where b.Name.LocalName == "body"
select b;
IEnumerable<XElement> sections = from s in body.Descendants()
where s.Name.LocalName == "sections"
select s;
IEnumerable<XElement> paragraphs = from p in sections.Descendants()
where p.Name.LocalName == "para"
select p;
string bodytext = "";
if (paragraphs.Count() > 0)
{
StringBuilder text = new StringBuilder();
foreach (XElement p in paragraphs)
{
text.AppendFormat("{0} ", p.Value);
}
}
bodytext = text.ToString();

Using an XmlTextAttribute on an array of mixed types removes whitespace strings

I am writing a set of objects that must serialize to and from Xml, following a strict specification that I cannot change. One element in this specification can contain a mix of strings and elements in-line.
A simple example of this Xml output would be this:
<root>Leading text <tag>tag1</tag> <tag>tag2</tag></root>
Note the whitespace characters between the closing of the first tag, and the start of the second tag. Here are the objects that represents this structure:
[XmlRoot("root")]
public class Root
{
[XmlText(typeof(string))]
[XmlElement("tag", typeof(Tag))]
public List<object> Elements { get; set; }
//this is simply for the sake of example.
//gives us four objects in the elements array
public static Root Create()
{
Root root = new Root();
root.Elements.Add("Leading text ");
root.Elements.Add(new Tag() { Text = "tag1" });
root.Elements.Add(" ");
root.Elements.Add(new Tag() { Text = "tag2" });
return root;
}
public Root()
{
Elements = new List<object>();
}
}
public class Tag
{
[XmlText]
public string Text {get;set;}
}
Calling Root.Create(), and saving to a file using this method looks perfect:
public XDocument SerializeToXml(Root obj)
{
XmlSerializer serializer = new XmlSerializer(typeof(Root));
XDocument doc = new XDocument();
using (var writer = doc.CreateWriter())
{
serializer.Serialize(writer, obj);
}
return doc;
}
Serialization looks exactly like the xml structure at the beginning of this post.
Now when I want to serialize an xml file back into a Root object, I call this:
public static Root FromFile(string file)
{
XmlSerializer serializer = new XmlSerializer(typeof(Root));
XmlReaderSettings settings = new XmlReaderSettings();
XmlReader reader = XmlTextReader.Create(file, settings);
//whitespace gone here
Root root = serializer.Deserialize(reader) as Root;
return root;
}
The problem is here. The whitespace string is eliminated. When I call Root.Create(), there are four objects in the Elements array. One of them is a space. This serializes just fine, but when deserializing, there are only 3 objects in Elements. The whitespace string gets eliminated.
Any ideas on what I'm doing wrong? I've tried using xml:space="preserve", as well as a host of XmlReader, XmlTextReader, etc. variations. Note that when I use a StringBuilder to read the XmlTextReader, the xml contains the spaces as I'd expect. Only when calling Deserialize(stream) do I lose the spaces.
Here's a link to an entire working example. It's LinqPad friendly, just copy/paste: http://pastebin.com/8MkUQviB The example opens two files, one a perfect serialized xml file, the second being a deserialized then reserialized version of the first file. Note you'll have to reference System.Xml.Serialization.
Thanks for reading this novel. I hope someone has some ideas. Thank you!
It looks like a bug. Workaround seems to be replace all whitespaces and crlf in XML text nodes by
entities. Semantic equal entities (
) does not work.
<root>Leading text <tag>tag1</tag> <tag>tag2</tag></root>
is working for me.

Better way to browse XML nodes?

I'm playing with an imported XML file and using XMLDocument I'm wondering if there is a better way to do the same thing.
Basically root contains MHZ nodes and each MHZ contains several devices and one name.
I want to count every MHZ nodes and display the number of devices in each MHZ :
String xmlName = "tts.xml";
XmlDocument readDoc = new XmlDocument();
readDoc.Load(xmlName);
int fileNb = readDoc.SelectNodes("//MHZ").Count;
Console.WriteLine("MHZ number : "+fileNb);
for (int i = 0; i < fileNb; i++)
{
int deviceNb = readDoc.SelectNodes("//MHZ[" +(i+1)+ "]/device").Count;
Console.WriteLine(deviceNb);
}
If you're using .NET 3.5 or later, I'd use LINQ to XML:
var doc = XDocument.Load(xmlName);
var mhzs = doc.Descendants("MHZ");
Console.WriteLine("Count of MHZ: {0}", mhz.Count());
foreach (var mhz in mhzs)
{
Console.WriteLine(mhz.Elements("device").Count());
}
Have you had a look at Linq to XML
for more info check here
http://msdn.microsoft.com/en-us/library/bb387098.aspx
Create serializable objects and then deserialize your xml into a list of the objects. This way you can stay true to OOP.
[DataContract]
public class Device
{
[DataMember]
public string Property { get; set; }
}
Or look at: DataContract XML serialization and XML attributes or How to Deserialize XML document
In some cases I create object(s) that represents the structure of the XML and deserialize the XML to these object(s) This gives me a much better way of looking at the data, validating and iterating through collections of data.
XMLDocument is easy to use as your are already using it. Also it's worth to look at XPath

XML deserialization from XSD with variable XML elements

I have been given an XSD file that represents a huge number of elements and associated attributes. I have created an CS class using xsd.exe.
The issue is that the xml that is created can contain any or all elements and attributes.
Example XML:
<App action="A" id="1" validate="yes"><ProductType id="5885"/><SpecType id="221"/><Qty>1</Qty><PartType id="7212"/><Part>456789</Part></App>
<App action="A" id="2" validate="yes"><ProductType id="5883"/><Qty>1</Qty><PartType id="7211"/><Part>132465</Part></App>
Then in my code:
protected static void ImportProduct(string filename)
{
var counter = 0;
var xSerializer = new XmlSerializer(typeof(ProductList));
var fs = new FileStream(String.Format("{0}{1}", FilePath, filename), FileMode.Open);
var reader = XmlReader.Create(fs);
var items = (ProductList)xSerializer.Deserialize(reader);
foreach (var record in items.App)
{
counter++;
Console.Write(String.Format("{0}{1}", record.ProductType.id, Environment.NewLine));
Console.Write(String.Format("{0}{1}", record.Part.Value, Environment.NewLine));
*if (!record.SpecType.Value.Equals(null))
Console.Write(String.Format("{0}{1}", record.SpecType.id, Environment.NewLine));
else
Console.Write(String.Format("{0}{1}", "No SpecType!", Environment.NewLine));
if (counter == 10)
break;
}
}
So my question is how I can check for an empty/ non-existent element, per the starred (*) line above.
I cannot change the xsd or source XML files in any way, as they are produced by major manufacturers.
Let me know if you need more information.
Thanks! Brad
Sorry, XSD.EXE and XML Serialization isn't going to deal with XML like that.
XML of that nature is created because someone thinks it should be easy for humans to read and type in. They don't think about whether machines will be able to use them. It's a mistake that you'll now have to pay for.
The best you could do would be to create an XSLT that will place the elements into some canonical order, then create an XSD representing that order and create classes from the XSD.
Once you have an XSD you could use the dataset instead of the XML Reader. Then there are a few automatic methods created to check nulls as seen in the below example.
eg. This in an example where CalcualtionAnalysisDS is the XSD.
CalcualtionAnalysisDS ds = new CalcualtionAnalysisDS();
ds.ReadXml("calc.xml");
foreach (CalcualtionAnalysisDS.ReportRow row in ds.Report.Rows)
{
if (row.IsBestSHDSLDesignClassNull)
{
}
}

Categories

Resources