I'm following the tutorial given here for deserializing an embedded xml document.
My Xml doc:
<?xml version="1.0" encoding="utf-8" ?>
<ArrayOfAgency xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<DbAgencyDefinition>
<Name>RTD</Name>
<Country>USA</Country>
<City>Denver</City>
<State>CO</State>
<GtfsZipUrlDirectory>http://www.address.com/etc/</GtfsZipUrlDirectory>
<GtfsZipUrlFileName>file_name.zip</GtfsZipUrlFileName>
</DbAgencyDefinition>
</ArrayOfAgency>
My class I'm deserializing to:
public class DbAgencyDefinition
{
public string Name { get; set; }
public string City { get; set; }
public string Country { get; set; }
public string GtfsZipUrlDirectory { get; set; }
public string GtfsZipUrlFileName { get; set; }
public string State { get; set; }
}
The code that's trying to deserialize the XML to a list of DbAgencyDefinition:
var assembly = IntrospectionExtensions.GetTypeInfo(typeof(DbAgencyDefinition)).Assembly;
Stream stream = assembly.GetManifestResourceStream("MyNamespace.Resources.xml.AgencyDefinitions.xml");
var agencies = new List<DbAgencyDefinition>();
using (var reader = new StreamReader(stream))
{
var serializer = new XmlSerializer(typeof(List<DbAgencyDefinition>));
agencies = (List<DbAgencyDefinition>)serializer.Deserialize(reader);
}
The error I'm getting is:
System.Exception: There is an error in XML document. <ArrayOfAgency xmlns=''> was not expected
I've tried a million things with the XML, marking the class as Serializable, and it always returns this error. I looked at the code samples that the tutorial gives and I can't figure out why I'm getting this error.
VS for Windows, and maybe on Mac as well, has a special tool that will convert copied Xml into autogenerated classes. Now, it's not perfect but if you take your Xml file it generates a couple of classes similar to this:
public class ArrayOfAgency
{
public ArrayOfAgencyDbAgencyDefinition DbAgencyDefinition { get; set; }
}
public class ArrayOfAgencyDbAgencyDefinition
{
public string Name { get; set; }
public string Country { get; set; }
public string City { get; set; }
public string State { get; set; }
public string GtfsZipUrlDirectory { get; set; }
public string GtfsZipUrlFileName { get; set; }
}
As you might notice ArrayOfAgency is determined as a class holding a DbAgencyDefinition, which is why it's throwing an error while trying to deserialize it directly into a List<DbAgencyDefinition>. The type and what the serializer is expecting are not quite the same.
var serializer = new XmlSerializer(typeof(ArrayOfAgency));
var agencies = ((ArrayOfAgency)serializer.Deserialize(reader)).DbAgencyDefinition;
Also, as I mentioned the auto-generation may not be perfect because ArrayOfAgency may need to hold an array instead of a direct class if there can be more than one DbAgencyDefinition possible in Xml.
public class ArrayOfAgency
{
public ArrayOfAgencyDbAgencyDefinition[] DbAgencyDefinition { get; set; }
}
If you need more help or info on Xml Serialization check out the docs.
Try following :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
namespace ConsoleApplication120
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
using (var reader = new StreamReader(FILENAME, Encoding.UTF8))
{
var serializer = new XmlSerializer(typeof(ArrayOfAgency));
ArrayOfAgency agencies = (ArrayOfAgency)serializer.Deserialize(reader);
}
}
}
public class ArrayOfAgency
{
public DbAgencyDefinition DbAgencyDefinition { get; set; }
}
public class DbAgencyDefinition
{
public string Name { get; set; }
public string City { get; set; }
public string Country { get; set; }
public string GtfsZipUrlDirectory { get; set; }
public string GtfsZipUrlFileName { get; set; }
public string State { get; set; }
}
}
Related
I'm trying to post my data as XML to my asp.net core 3.1 web api. However Collection properties are not getting binded in my model.
Here is my class,
public class Test
{
public int Usrno { get; set; }
public string PCname { get; set; }
public List<Best> Best { get; set; }
}
public class Best
{
public string Hello { get; set; }
public Worst[] Worst { get; set; }
}
public class Worst
{
public int Ko { get; set; }
public Win[] Win { get; set; }
}
public class Win
{
public string Kiss { get; set; }
}
Here is my POST end point,
[HttpPost]
[Consumes("application/xml")]
[Produces("application/xml")]
public IActionResult Create([FromBody]Test data)
{
return Created("", data);
}
Here is my XML input,
<?xml version="1.0" encoding="UTF-8"?>
<Test>
<Usrno>0</Usrno>
<PCname>string</PCname>
<Best>
<Hello>string</Hello>
<Worst>
<Ko>0</Ko>
<Win>
<Kiss>string</Kiss>
</Win>
</Worst>
</Best>
</Test>
Here is the screen print of the POST method in API,
Here is my ConfigureServices in Startup.cs,
services
.AddControllers()
.AddJsonOptions(options => { options.JsonSerializerOptions.PropertyNamingPolicy = null; })
.AddXmlSerializerFormatters()
.AddXmlDataContractSerializerFormatters();
I couldn't figure out what I'm missing. Please assist
Try using a XmlElementAttribute on Best element.
XmlElement attribute indicates that a public field or property represents an XML element when the XmlSerializer serializes or deserializes the object that contains it.
C#
public class Test
{
public int Usrno { get; set; }
public string PCname { get; set; }
[XmlElement("Best")]
public List<Best> Best { get; set; }
}
The MVC's XmlSerializerInputFormatter calls XmlSerializer to deserialize the body of the request and the formetter uses this attribute to mark XML elements.
An array or List in xml serialization expects two tags like "Names" and "Name". You only have one tag so you need to add the attribute XmlElement. This issue occurs in multiple places in your classes. I fixed all the issue. See classes below
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XmlReader reader = XmlReader.Create(FILENAME);
XmlSerializer serializer = new XmlSerializer(typeof(Test));
Test test = (Test)serializer.Deserialize(reader);
}
}
public class Test
{
public int Usrno { get; set; }
public string PCname { get; set; }
[XmlElement("Best")]
public List<Best> Best { get; set; }
}
public class Best
{
public string Hello { get; set; }
[XmlElement("Worst")]
public Worst[] Worst { get; set; }
}
public class Worst
{
public int Ko { get; set; }
[XmlElement("Win")]
public Win[] Win { get; set; }
}
public class Win
{
public string Kiss { get; set; }
}
}
I have the following class:
[XmlType("supervisor")]
public class Supervisor
{
[XmlAttribute("id")]
public string Id { set; get; }
[XmlElement("Name")]
public string Name { set; get; }
[XmlElement("Contract")]
public int Contracts { set; get; }
[XmlElement("Volume")]
public long Volume { set; get; }
[XmlElement("Average")]
public int Average { set; get; }
}
which reads from XML file:
<digital-sales xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<supervisor id="1236674">
<Name>Hiroki</Name>
<Contract>11</Contract>
<Volume>1036253</Volume>
<Average>94205</Average>
</supervisor>
<supervisor id="123459">
<Name>Ayumi</Name>
<Contract>5</Contract>
<Volume>626038</Volume>
<Average>125208</Average>
</supervisor> ...
</digital-sales>
in the code I create List and process it.
now I want to write the List to XML file while maintaining the
same XML structure. How do I do that?
How to use xml id to fill class object?
Here is the code :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
namespace ConsoleApplication98
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XmlReader reader = XmlReader.Create(FILENAME);
XmlSerializer serializer = new XmlSerializer(typeof(DigitalSales));
DigitalSales digitalSales = (DigitalSales)serializer.Deserialize(reader);
reader.Close();
XmlWriter writer = XmlWriter.Create(FILENAME);
serializer.Serialize(writer, digitalSales);
}
}
[XmlRoot("digital-sales")]
public class DigitalSales
{
[XmlElement("supervisor")]
public List<Supervisor> supervisor { get; set; }
}
[XmlRoot("supervisor")]
public class Supervisor
{
[XmlAttribute("id")]
public string Id { set; get; }
[XmlElement("Name")]
public string Name { set; get; }
[XmlElement("Contract")]
public int Contracts { set; get; }
[XmlElement("Volume")]
public long Volume { set; get; }
[XmlElement("Average")]
public int Average { set; get; }
}
}
I am looking into loading a large number of InfoPath XML files into Cosmos DB as Json files to test a possible project. But I am struggling with converting the XML files to Json and then loading back into a c# class. I think this is related to the namespace that InfoPath puts into the XML but I'm not sure. I thought at first I should try to remove all the namespace references from the XML but now I'm wondering if I just need to work more on the definition of the class file. To generate the class file I used Visual Studio "paste as Json". I assumed after loading the XML, saving to Json, then using that as the basis for the new class file, I could load right back into the object but when I call DeserializeObject it always ends up null.
Is this because the class file definition needs to be changed or should I instead look to try and clean the XML prior to converting to Json?
Sample XML
<?xml version="1.0" encoding="utf-8"?>
<?mso-infoPathSolution name="urn:schemas-microsoft-com:office:infopath:myProject:-myXSD-2017-05-05T14-19-13" solutionVersion="1.0.0.2046" productVersion="16.0.0.0" PIVersion="1.0.0.0" href="https://myportal.sharepoint.com/sites/mySite/myProject/Forms/template.xsn"?>
<?mso-application progid="InfoPath.Document" versionProgid="InfoPath.Document.4"?>
<?mso-infoPath-file-attachment-present?>
<my:myFields xmlns:my="http://schemas.microsoft.com/office/infopath/2003/myXSD/2017-05-05T14:19:13" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:pc="http://schemas.microsoft.com/office/infopath/2007/PartnerControls" xmlns:ma="http://schemas.microsoft.com/office/2009/metadata/properties/metaAttributes" xmlns:d="http://schemas.microsoft.com/office/infopath/2009/WSSList/dataFields" xmlns:q="http://schemas.microsoft.com/office/infopath/2009/WSSList/queryFields" xmlns:dfs="http://schemas.microsoft.com/office/infopath/2003/dataFormSolution" xmlns:dms="http://schemas.microsoft.com/office/2009/documentManagement/types" xmlns:tns="http://microsoft.com/webservices/SharePointPortalServer/UserProfileService" xmlns:s1="http://microsoft.com/wsdl/types/" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xd="http://schemas.microsoft.com/office/infopath/2003" xml:lang="en-US">
<my:Admin>
<my:Routing_Order>
<my:Approver-1_Order>1</my:Approver-1_Order>
<my:Approver-2_Order>5</my:Approver-2_Order>
<my:Approver-3_Order>4</my:Approver-3_Order>
</my:Routing_Order>
</my:Admin>
<my:Request_Status>Save as Draft</my:Request_Status>
<my:Request_Type>CAPEX</my:Request_Type>
</my:myFields>
Class file
namespace xml_to_json
{
public class Rootobject
{
public MyMyfields mymyFields { get; set; }
}
public class MyMyfields
{
public string xmlnsmy { get; set; }
public string xmlnsxsi { get; set; }
public string xmlnsxhtml { get; set; }
public string xmlnspc { get; set; }
public string xmlnsma { get; set; }
public string xmlnsd { get; set; }
public string xmlnsq { get; set; }
public string xmlnsdfs { get; set; }
public string xmlnsdms { get; set; }
public string xmlnstns { get; set; }
public string xmlnss1 { get; set; }
public string xmlnshttp { get; set; }
public string xmlnstm { get; set; }
public string xmlnssoap { get; set; }
public string xmlnssoapenc { get; set; }
public string xmlnsmime { get; set; }
public string xmlnssoap12 { get; set; }
public string xmlnswsdl { get; set; }
public string xmlnsxd { get; set; }
public string xmllang { get; set; }
public MyAdmin myAdmin { get; set; }
public string myRequest_Status { get; set; }
public string myRequest_Type { get; set; }
}
public class MyAdmin
{
public MyRouting_Order myRouting_Order { get; set; }
}
public class MyRouting_Order
{
public string myApprover1_Order { get; set; }
public string myApprover2_Order { get; set; }
public string myApprover3_Order { get; set; }
}
}
Console test code
namespace xml_to_json
{
class Program
{
static void Main(string[] args)
{
XmlDocument myDoc = new XmlDocument();
string sourcedir = #"C:\Users\mystuff\Downloads\test-clean\";
string xmlfilein = #"test";
string xmlfile = string.Concat(sourcedir, xmlfilein, ".xml");
string jsonfileout = string.Concat(sourcedir, xmlfilein, ".json");
myDoc.Load(xmlfile);
string jsonText = JsonConvert.SerializeXmlNode(myDoc.LastChild);
File.WriteAllText(jsonfileout, jsonText);
Rootobject obj = new Rootobject();
obj = JsonConvert.DeserializeObject<Rootobject>(jsonText);
Console.WriteLine(obj.mymyFields.myRequest_Status);
}
}
}
Try code below using xml linq :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
XElement routingOrder = doc.Descendants().Where(x => x.Name.LocalName == "Routing_Order").FirstOrDefault();
Dictionary<string, string> orders = routingOrder.Elements()
.GroupBy(x => x.Name.LocalName, y => (string)y)
.ToDictionary(x => x.Key, y => y.FirstOrDefault());
string status = (string)doc.Descendants().Where(x => x.Name.LocalName == "Request_Status").FirstOrDefault();
string requestType = (string)doc.Descendants().Where(x => x.Name.LocalName == "Request_Type").FirstOrDefault();
}
}
}
Using suggestion from jdweng and XDocument helped to clean the XML file for easier transition to Json. Below is a sample of code I used to clean the document.
XDocument newxdoc = XDocument.Load(xmlfile);
newxdoc.DescendantNodes().Where(e => e.NodeType.ToString().StartsWith("ProcessingInstruction")).Remove();
string newstringdoc = RemoveAllNamespaces(newxdoc.ToString());
newxdoc = XDocument.Parse(newstringdoc);
I have the XML below:
<y:input xmlns:y='http://www.blahblah.com/engine/42'>
<y:datas>
<y:instance yclass='ReportPeriod' yid="report">
<language yid='en'/>
<threshold>0.6</threshold>
<typePeriod>predefinedPeriod</typePeriod>
<interval>month</interval>
<valuePeriod>April</valuePeriod>
<fund yclass="Fund">
<name>K</name>
<indexName>CAC40</indexName>
</fund>
</y:instance>
</y:datas>
</y:input>
That I am trying to deserialize to
[XmlRoot(ElementName="fund")]
public class Fund
{
[XmlElement(ElementName="name")]
public string Name { get; set; }
[XmlElement(ElementName="indexName")]
public string IndexName { get; set; }
[XmlAttribute(AttributeName="yclass")]
public string Yclass { get; set; }
}
[XmlRoot(ElementName="instance", Namespace="http://www.blahblah.com/engine/42")]
public class Instance
{
[XmlElement(ElementName="language")]
public Language Language { get; set; }
[XmlElement(ElementName="threshold")]
public string Threshold { get; set; }
[XmlElement(ElementName="typePeriod")]
public string TypePeriod { get; set; }
[XmlElement(ElementName="interval")]
public string Interval { get; set; }
[XmlElement(ElementName="valuePeriod")]
public string ValuePeriod { get; set; }
[XmlElement(ElementName="fund")]
public Fund Fund { get; set; }
[XmlAttribute(AttributeName="yclass")]
public string Yclass { get; set; }
[XmlAttribute(AttributeName="yid")]
public string Yid { get; set; }
}
[XmlRoot(ElementName="datas", Namespace="http://www.blahblah.com/engine/42")]
public class Datas
{
[XmlElement(ElementName="instance", Namespace="http://www.blahblah.com/engine/42")]
public Instance Instance { get; set; }
}
[XmlRoot(ElementName="input", Namespace="http://www.blahblah.com/engine/42")]
public class Input
{
[XmlElement(ElementName="datas", Namespace="http://www.blahblah.com/engine/42")]
public Datas Datas { get; set; }
[XmlAttribute(AttributeName="y", Namespace="http://www.blahblah.com/engine/42", Form = XmlSchemaForm.Qualified)]
public string Y { get; set; }
}
However, when deserializing the XML above:
public static class Program
{
public static void Main(params string[] args)
{
var serializer = new XmlSerializer(typeof(Input));
using (var stringReader = new StringReader(File.ReadAllText("file.xml")))
{
using(var xmlReader = XmlReader.Create(stringReader))
{
var instance = (Input)serializer.Deserialize(stringReader);
}
}
}
}
I get an error due to the y prefix...
There is an error in XML document (1, 1). ---> System.Xml.XmlException: Data at the root level is invalid. Line 1, position 1.
Reading some posts like that one: https://stackoverflow.com/a/36163079/4636721 it seems that there is maybe a bug with the XmlSerializer.
The cause of the exception is that you are passing stringReader rather than xmlReader to serializer.Deserialize(). You should be passing the XML reader instead:
Input instance = null;
var serializer = new XmlSerializer(typeof(Input));
using (var stringReader = new StreamReader("file.xml"))
{
using(var xmlReader = XmlReader.Create(stringReader))
{
instance = (Input)serializer.Deserialize(xmlReader);
}
}
(Apparently XmlReader.Create(stringReader) advances the text reader a bit, so if you later attempt to read from the stringReader directly, it has been moved past the root element.)
You also have some errors in your data model. It should look like:
[XmlRoot(ElementName="fund")]
public class Fund
{
[XmlElement(ElementName="name")]
public string Name { get; set; }
[XmlElement(ElementName="indexName")]
public string IndexName { get; set; }
[XmlAttribute(AttributeName="yclass")]
public string Yclass { get; set; }
}
[XmlRoot(ElementName="instance")]
[XmlType(Namespace = "")] // Add this
public class Instance
{
[XmlElement(ElementName="language")]
public Language Language { get; set; }
[XmlElement(ElementName="threshold")]
public string Threshold { get; set; }
[XmlElement(ElementName="typePeriod")]
public string TypePeriod { get; set; }
[XmlElement(ElementName="interval")]
public string Interval { get; set; }
[XmlElement(ElementName="valuePeriod")]
public string ValuePeriod { get; set; }
[XmlElement(ElementName="fund")]
public Fund Fund { get; set; }
[XmlAttribute(AttributeName="yclass")]
public string Yclass { get; set; }
[XmlAttribute(AttributeName="yid")]
public string Yid { get; set; }
}
[XmlRoot(ElementName="datas", Namespace="http://www.blahblah.com/engine/42")]
public class Datas
{
[XmlElement(ElementName="instance", Namespace="http://www.blahblah.com/engine/42")]
public Instance Instance { get; set; }
}
[XmlRoot(ElementName="input", Namespace="http://www.blahblah.com/engine/42")]
public class Input
{
[XmlElement(ElementName="datas", Namespace="http://www.blahblah.com/engine/42")]
public Datas Datas { get; set; }
//Remove This
//[XmlAttribute(AttributeName="y", Namespace="http://www.blahblah.com/engine/42", Form = XmlSchemaForm.Qualified)]
//public string Y { get; set; }
}
// Add this
[XmlRoot(ElementName="language")]
public class Language
{
[XmlAttribute(AttributeName="yid")]
public string Yid { get; set; }
}
Notes:
xmlns:y='http://www.blahblah.com/engine/42' is an XML namespace declaration and thus should not be mapped to a member in the data model.
The child elements of <y:instance ...> are not in any namespace. Unless the namespace of the child elements is specified by attributes somehow, XmlSerializer will assume that they should be in the same namespace as the containing element, here http://www.blahblah.com/engine/42".
Thus it is necessary to add [XmlType(Namespace = "")] to Instance to indicate the correct namespace for all child elements created from Instance. (Another option would be to add [XmlElement(Form = XmlSchemaForm.Unqualified)] to each member, but I think it is easier to set a single attribute on the type.)
A definition for Language is not included in your question, so I included one.
It will be more efficient to deserialize directly from your file using a StreamReader than to read first into a string, then deserialize from the string using a StringReader.
Working sample fiddle here.
I have an issue with my JSON to XML code. It's not assigning the values to the Object and I cannot figure out why. Please let me know what I am doing wrong.
My C# code:
using Newtonsoft.Json;
using System.Xml;
namespace JSONTest
{
public class Program
{
static void Main(string[] args)
{
string fileName = "C:\\Code\\JSONTest\\data\\response.xml";
// Convert XML Data into JSON Data
XmlDocument xmlFile = new XmlDocument();
xmlFile.Load(fileName);
string jsonData = JsonConvert.SerializeXmlNode(xmlFile);
// Convert JSON Data into Object
RootObject root = JsonConvert.DeserializeObject<RootObject>(jsonData);
var data = root.RESPONSE_GROUP;
Console.ReadLine();
}
}
public class RootObject
{
public RESPONSEGROUP RESPONSE_GROUP { get; set; }
}
public class RESPONSEGROUP
{
public string MISMOVersionID { get; set; }
public object RESPONDING_PARTY { get; set; }
public object RESPOND_TO_PARTY { get; set; }
public RESPONSE RESPONSE { get; set; }
}
public class RESPONSE
{
public string ResponseDateTime { get; set; }
public KEY KEY { get; set; }
public STATUS STATUS { get; set; }
}
public class KEY
{
public string _Name { get; set; }
public string _Value { get; set; }
}
public class STATUS
{
public string _Code { get; set; }
public string _Condition { get; set; }
public string _Description { get; set; }
public string _Name { get; set; }
}
}
XML
<RESPONSE_GROUP MISMOVersionID="2.4">
<RESPONDING_PARTY/>
<RESPOND_TO_PARTY/>
<RESPONSE ResponseDateTime="2015-02-19T10:32:11-06:00">
<KEY _Name="LOSClientID" _Value="3000799866"/>
<STATUS _Code="S0010" _Condition="Success" _Description="TEST DESC" _Name="Complete"/>
</RESPONSE>
</RESPONSE_GROUP>
My "JSONData" string:
{"RESPONSE_GROUP":{"#MISMOVersionID":"2.4","RESPONDING_PARTY":null,"RESPOND_TO_PARTY":null,"RESPONSE":{"#ResponseDateTime":"2015-02-19T10:32:11-06:00","KEY":{"#_Name":"LOSClientID","#_Value":"3000799866"},"STATUS":{"#_Code":"S0010","#_Condition":"Success","#_Description":"THIS IS THE DESCRIPTION.","#_Name":"Complete"}}}}
The value of: root.RESPONSE_GROUP.MISMOVersionID is NULL as well as any other values that should have been populated. I know I'm doing something wrong here, but I cannot figure out what it is.
Please help! Thanks in advance.
The problem is that your JSON contains # signs in front of some property names. For example:
"#MISMOVersionID":"2.4"
There are two options here:
Fix the JSON to not have that, e.g. "#MISMOVersionID":"2.4"
Use JsonPropertyAttribute to tell Json.NET which property name to expect in the JSON, e.g.
[JsonProperty("#MISMOVersionID")]
public string MISMOVersionID { get; set; }