XmlSerializer Deserializing Array/List of Multiple Objects at Root - c#

I'm trying to deserialize the following XML output:
<?xml version="1.0" encoding="ISO-8859-1"?>
<Foo>
<Val>Data1</Val>
</Foo>
<Foo>
<Val>Data2</Val>
</Foo>
(This is output from a hardware device, and cannot be changed)
I have an XML type defined as:
[XmlType(AnonymousType=true, Namespace="")]
public class Foo
{
public string Val { get; set; }
}
I've tried to deserialize this array by creating a serializer like:
var s = new XmlSerializer(typeof(Foo[]));
//or
var s = new XmlSerializer(typeof(List<Foo>);
But every call to s.Deserialize() causes an InvalidOperaitonException:
System.InvalidOperationException: <Foo xmlns=''> was not expected.
Note
var s = new XmlSerializer(typeof(Foo));
// Only deseralizes the first Foo (Data1).
Thanks for your help.

I think the issue is with your provided xml.
Test app says
List<Foo> list = new List<Foo> {new Foo {Val = "Data1"}, new Foo {Val = "Data2"}};
var s = new XmlSerializer(typeof(List<Foo>));
StringBuilder sb = new StringBuilder();
XmlWriter wr = XmlWriter.Create(sb);
s.Serialize(wr, list);
string ss = sb.ToString();
var s2 = new XmlSerializer(typeof(List<Foo>));
StringReader sr = new StringReader(ss);
List<Foo> returnList = (List<Foo>)s2.Deserialize(sr);
And the XML should be
<?xml version="1.0" encoding="utf-16"?>
<ArrayOfFoo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Foo>
<Val>Data1</Val>
</Foo>
<Foo>
<Val>Data2</Val>
</Foo>
</ArrayOfFoo>
If you can remove the inital line
<?xml version="1.0" encoding="ISO-8859-1"?>
And minipulate the string into
string s = "<ArrayOfFoo><Foo> <Val>Data1</Val></Foo><Foo> <Val>Data2</Val></Foo></ArrayOfFoo>";
var s2 = new XmlSerializer(typeof(List<Foo>));
StringReader sr = new StringReader(s);
List<Foo> list = (List<Foo>)s2.Deserialize(sr);
That could work.

That isn't valid Xml. There needs to be a core root element for it to work properly.

this is not a valid xml so you can not deserialize it like a valid xml. You need some kind of hack to make this work. i'd suggest to insert at beginning of the xml and inserting at the end of xml. then you can deserialize it, since you cant make this change at xml side, do it in your code.
String ss;
// lets assume this holds your xml data in string.
ss.append("</ArrayOfFoo>");
ss.replace("<?xml version=\"1.0\" encoding=\"utf-16\"?>", "<?xml version=\"1.0\" encoding=\"utf-16\"?> <ArrayOfFoo>")
var s2 = new XmlSerializer(typeof(List<Foo>));
StringReader sr = new StringReader(ss);
List<Foo> returnList = (List<Foo>)s2.Deserialize(sr);
now this shall return you the correct list.

As the other posters say, this XML that the hardware device produces is not compatible to the way .NET serializes/deserializes object. Valid XML, and .NET requires valid XML, has a root element.
I suggest:
either you modify your obtained XML to match the way astander presents in his xml code snippet.
or you write a simple xml parser for your file that deserializes the file like you need
br, Marcel

Technically, what you have there is not a well-formed XML document (which has exactly one root element), but rather an well-formed external parsed entity (which can have any number of elements not contained in other elements, as well as text not contained in any elements). Therefore, it should parse if your XML parser has an entry point for parsing EPEs rather than documents. You could also create a stub document which includes by reference your EPE and parse that document.

Xstream for .Net could be a useful API

Related

LINQ to XML in c# . Enumeration yielded no results?

I'm currently trying to retrieve data from a web service, I would like to do search on results if the score is over 90 for example. I have tried to bring back results without doing searches also and getting no results. Could someone please give me a hand in where I'm going wrong?
FundNamesPayload xmlresponse = new FundNamesPayload();
xmlresponse = search.SearchByName("Australiansuper", "GUID-Here", "Y");
MemoryStream XmlStream = new MemoryStream();
StreamReader XmlReader = new StreamReader(XmlStream);
XmlSerializer Serializer = new XmlSerializer(typeof(FundNamesPayload));
Serializer.Serialize(XmlStream, xmlresponse);
XmlStream.Seek(0, System.IO.SeekOrigin.Begin);
var str = XElement.Parse(XmlReader.ReadToEnd());
var Matching = from data in str.Descendants("FundName")
where(int)data.Element("Score") > 90
select data;
Here is an example of the XML
<SuperFundNamesPayload xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://superfundlookup.gov.au">
<Request>
<Guid>************</Guid>
<Name>HOST Plus</Name>
<ActiveFundsOnly>Y</ActiveFundsOnly>
</Request>
<Response>
<DateTimeRetrieved>2017-09-25T12:20:40.8446457+10:00</DateTimeRetrieved>
<MatchingFundNames>
<NumberOfRecords>2</NumberOfRecords>
<MatchingFundName>
<ABN>
<Value>68657495890</Value>
<IdentifierStatus>Active</IdentifierStatus>
</ABN>
<FundName>
<Name>THE TRUSTEE FOR HOST PLUS SUPERANNUATION FUND</Name>
<NameType>Entity Name</NameType>
<Score>94</Score>
<NameStatus>Current</NameStatus>
</FundName>
<Location>
<StateTerritoryCode>VIC</StateTerritoryCode>
<Postcode>3000</Postcode>
</Location>
</MatchingFundName>
<MatchingFundName>
<ABN>
<Value>80567702967</Value>
<IdentifierStatus>Active</IdentifierStatus>
</ABN>
<FundName>
<Name>The Trustee for HOIST HYDRAULICS VIC P L SUPER FUND</Name>
<NameType>Entity Name</NameType>
<Score>73</Score>
<NameStatus>Current</NameStatus>
</FundName>
<Location>
<StateTerritoryCode>VIC</StateTerritoryCode>
<Postcode>3137</Postcode>
</Location>
</MatchingFundName>
</MatchingFundNames>
</Response>
</SuperFundNamesPayload>
The problem is that the XML document specifies a default namespace:
<SuperFundNamesPayload ... xmlns="http://superfundlookup.gov.au">
So you must specify that namespace when you look up elements:
XNamespace ns = "http://superfundlookup.gov.au";
var Matching = from data in str.Descendants(ns + "FundName")
where (int)data.Element(ns + "Score") > 90
select data;
There a couple atypical features of the LINQ to XML syntax:
You create an XNamespace object by using its implicit string conversion operator instead of new.
You create a namespace-qualified name (an XName) by concatenating an XNamespace object and a string with the + operator.
I had a similar issue because I have forgot to write the constructor (the 'var xmlresponse = new FundNamesPayload();' line). I've got no exception, no error, just an empty (null) result..

Append New Element in XML VB or C#

I apologize if this question is too easy for you as I am just starting to learn how XML works. What I am trying to do is to append a new element to App/Library and for example I started with this.
<?xml version="1.0" encoding="utf-8"?>
<App>
<Library>
<Count>100</Count>
<Track>
<TrackID>1</TrackID>
<Name>New York</Name>
<Artist>Snow Patrol</Name>
</Track>
</Library>
</App>
what I am trying to do is to add another track to my existing XML.
<Track>
<TrackID>2</TrackID>
<Name>Chasing Cars</Name>
<Artist>Snow Patrol</Name>
</Track>
There are a couple of ways to deal with xml manipulation in .NET. The older style using System.Xml.XmlDocument or using System.Xml.Linq.XDocument approach.
Here is an example on how to do it using XmlDocument:
var xml = "Baz";
var doc = new XmlDocument();
doc.LoadXml(xml); //Load up the original xml string into XmlDocument object
var fooNode = doc.SelectSingleNode("//foo"); //use xpath to get to the top level foo element
var newBar = doc.CreateElement("bar"); //Create a new bar element and assign it's inner text
newBar.InnerText = "Baz2";
fooNode.AppendChild(newBar); //append the newly created bar element to foo
and here is an example on how to do it using XDocument (which is much more simpler):
var xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?><foo><bar>Baz</bar></foo>";
var xdoc = XDocument.Parse(xml); //Load up the original xml string into Linq XDocument object
xdoc.Root.Add(new XElement("bar", "Bar2")); //Add <bar>Bar2</bar> to foo (which is root of the document)
Here is a running example:
https://dotnetfiddle.net/vY7Vag

XElement parses my XML file as one huge element, how do I fix?

So I am trying to parse through some XML which is being returned from a REST API call. The XML looks like this (with many more <link>'s of course):
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<response status="Ok">
<links>
<link id="79380" hint="Some Text" linkDescription="This is the GET url for this Customer." link="/customer/79380" httpMethod="GET"/>
</links>
</response>
I am loading the XML file using the following code:
Stream resStream = response.GetResponseStream();
StreamReader reader = new StreamReader(resStream);
XElement doc = XElement.Load(reader);
I then loop through the elements like so:
IEnumerable<XElement> List =
from el in doc.Descendants("links") select el;
foreach (XElement e in List)
{
test += e.ToString();
}
It only loops through once and test is just a string that contains the entire XML file. My goal is to get the value of the "id" attribute from each element and place them in a list.
I have tried various things and I can't seem to get anything back but one huge string.
Try this:
var idList = doc.Descendants("link").Select(x => (int)x.Attribute("id"));

xml Nodes by Element

Below is an example of the xml file that I need to pull data via C#. This is my first experience with reading xml files and a beginner with xml. Anyone have an example of how I would find/load the fieldorder values for Export_B?
<?xml version="1.0" encoding="utf-8"?>
<Config>
<OutFolderCSV>c:\Output\2012\upload_Files</OutFolderCSV>
<OutFolderImage>c:\Output\2012\NM_Scorecard_Images</OutFolderImage>
<PathOutLogFile>c:\Output\2012\Log\Matches.log</PathOutLogFile>
<FieldSeparator>,</FieldSeparator>
<ExportFile>
<Name>Export_A</Name>
<FieldOrder>matchID</FieldOrder>
<FieldOrder>contactID</FieldOrder>
<FieldOrder>stageID13</FieldOrder>
<FieldOrder>stringScore1a</FieldOrder>
<FieldOrder>xScore1a</FieldOrder>
<FieldOrder>stageID14</FieldOrder>
<FieldOrder>stringScore1b</FieldOrder>
<FieldOrder>xScore1b</FieldOrder>
<FieldOrder>stageID15</FieldOrder>
<FieldOrder>stringScore1c</FieldOrder>
<FieldOrder>xScore1c</FieldOrder>
</ExportFile>
<ExportFile>
<Name>Export_B</Name>
<FieldOrder>matchID</FieldOrder>
<FieldOrder>contactID</FieldOrder>
<FieldOrder>stageID16</FieldOrder>
<FieldOrder>stringScore1a</FieldOrder>
<FieldOrder>xScore1a</FieldOrder>
<FieldOrder>stageID17</FieldOrder>
<FieldOrder>stringScore1b</FieldOrder>
<FieldOrder>xScore1b</FieldOrder>
<FieldOrder>stageID18</FieldOrder>
<FieldOrder>stringScore1c</FieldOrder>
<FieldOrder>xScore</FieldOrder>
</ExportFile>
</Config>
Using LINQ to XML:
var doc = XDocument.Load(#"c:\path\to\file.xml");
var fieldOrders =
from exportFile in doc.Descendants("ExportFile")
where (string)exportFile.Element("Name") == "Export_B"
from fieldOrder in exportFile.Elements("FieldOrder")
select (string)fieldOrder;
I have written an article
http://www.codeproject.com/Articles/33769/Basics-of-LINQ-Lamda-Expressions
on XML using XDocument object.
You can parse the XML easily using
XDocument.Load(filepath)
Please read the section XLinq to parse the objects.
edit :
You can change value of Export_B using the code :
var document = XDocument.Load(filepath)
var exportFiles = document.Descandants("ExportFile");
List<XElement> list = new List<XElement>();
foreach(var element in exportFiles)
{
list.Add(element);
// Now you can do element.Element("Name") to get the name. Put a breakpoint on this, you can get the reference of all underlying objects.
}

Convert plain string to XML format

If I receive a string that is only a list of numbers (e.g. 1,2,3,5), is it possible to convert it to XML format, like this?
<foo>
<id>1</id>
<id>2</id>
<id>3</id>
</foo>
So far I had planned to use something along the lines of this
string s = "example";
XmlDocument xm = new XmlDocument();
xm.LoadXml(string.Format("<foo>{0}</foo>", s));
But I'm unsure as to how I should split the string so that I only get the numbers without using the obvious Split(), which is something my manager doesn't want me to do (otherwise I'd just skip the whole XML format).
Basically, is there a way for me to 'easily' serialize that string into XML format?
Use LINQtoXML
string items="1,4,6,3";
XElement elm = new XElement("foo");
foreach(var item in items.Split(','))
{
elm.Add(new XElement("id",item));
}
Now ele will have the XML you are looking for
You can use the XDocument.Parse(string) method
You can build up the xml string and then pass it to the method
http://msdn.microsoft.com/en-us/library/system.xml.linq.xdocument.parse.aspx

Categories