I am trying to read an XML file into an object in a C# project. Basing it off this article DON’T PARSE THAT XML! and this solution
Then type
xsd myFile.xsd /c
This will generate a set of classes that you can add to your project,
and then you can deserialize an xml file with this simple code:
1: XmlSerializer serializer = 2: new
XmlSerializer(typeof(MyFile)); 3: 4: Stream reader = new
FileStream("myFile.xml",FileMode.Open); 5: 6: MyFile myFile =
(MyFile) serializer.Deserialize(reader); It really is that simple.
There is no excuse for hand writing XML parsing code when you can
literally take an XML file you have never seen before and turn it into
an object in memory in 10 minutes. The serialization framework and XSD
tool provide options for using attributes to control how the XML is
generated also.
So I had already created my XSD and xsd myFile.xsd /c ran succeffuly.
In writing my code to serialize to object I keep getting XmlSerializer does not take a constructor with one argument.
When referring to the MSDN Class docs and the MSDN Examples my code looks like it should work, what is wrong?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization.XmlSerializer;
namespace testxmlc
{
public class XmlSerializer
{
class Program
{
static void Main (string filename)
{
XmlSerializer Serializer = new XmlSerializer(typeof(MyFile));
Stream reader = new FileStream("myFile.xml", FileMode.Open);
MyFile myFile = (MyFile)serializer.Deserialize(reader);
}
}
}
}
You are defining a class XmlSerializer that is conflicting with the .NET class XmlSerializer:
public class XmlSerializer
{
class Program
{
Also your usings are wrong. You need "using System.IO", and "using System.Xml.Serialization", and get rid of "using System.Xml.Serialization.XmlSerializer".
Related
I'm trying to embed an XML file into a C# console application via Right clicking on file -> Build Action -> Embedded Resource.
How do I then access this embedded resource?
XDocument XMLDoc = XDocument.Load(???);
Edit: Hi all, despite all the bashing this question received, here's an update.
I managed to get it working by using
XDocument.Load(new System.IO.StreamReader(System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream("Namespace.FolderName.FileName.Extension")))
It didn't work previously because the folder name containing the resource file within the project was not included (none of the examples I found seemed to have that).
Thanks everyone who tried to help.
Something along these lines
using System.IO;
using System.Reflection;
using System.Xml;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("ConsoleApplication1.XMLFile1.xml");
StreamReader reader = new StreamReader(stream);
XmlDocument doc = new XmlDocument();
doc.LoadXml(reader.ReadToEnd());
}
}
}
Here is a link to the Microsoft doc that describes how to do it. http://support.microsoft.com/kb/319292
really stuck, any help and/or comments would be greatly appreciated!
I've written a database program that needs to be able to load contents from a file into a list. So basically i'm trying to use serialization and deserialization for this. The area where the error occurs is in bold and is italicized:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
namespace ConsoleApplication1
{
class TheFile
{
//Version 1 serialiser, instance specific, so a bit of a pain
public void SerializeObject(string filename, TheDocument document)
{
Stream stream = File.Open(filename, FileMode.Create);
BinaryFormatter bFormatter = new BinaryFormatter();
bFormatter.Serialize(stream, document);
stream.Close();
}
public TheDocument DeSerializeObject(string filename)
{
TheDocument document;
Stream stream = File.Open(filename, FileMode.Open);
BinaryFormatter bFormatter = new BinaryFormatter();
***document = (TheDocument)bFormatter.Deserialize(stream);***
stream.Close();
return document;
}
}
}
The error which i receive is as follows: The input stream is not a valid binary format. The starting contents (in bytes) are: 31-37-2F-30-39-2F-32-30-31-31-20-31-31-3A-30-36-3A ...
I have seen this reported somewhere else recently, and I was unable to find an explanation there either. The code as presented looks like it should be fine (although it would benefit greatly from a few using statements, but they won't break the success case since you are calling .Close()).
However! I would also warn that IMO BinaryFormatter is not a good choice for storage in a database, since that suggests it is desirable to read it back in the future. The fact that BinaryFormatter is type-dependent makes it very very brittle as you version your application. Any of: creating a new app-version, renaming/adding/removing a field, changing a property to an automatically implemented property, changing .NET version, changing platform, ... could make your data either unreadable, or readable only by adding a lot of custom binder code.
I strongly suggest that you consider using a contract-based serializer instead of BinaryFormatter; any of: DataContractSerializer (but not NetDataContractSerializer), XmlSerializer, JavascriptSerializer, JSON.Net. If you want binary for size and performance, then protocol buffers (with several C# implementations, including protobuf-net) is designed (by Google) to be version tolerant, small, and fast. Since that list is also cross-platform, it also means your data is safe if, say, you switch platform to Java, Mono, WinRT (the new windows 8 subsystem), PHP, or anything else. BinaryFormatter will not work on any of those.
Does this succeed?
var path = "...";
var doc = new TheDocument(...);
SerializeObject(path, doc);
var restored = DeserializedObject(path);
Assert.IsNotNull(restored); // NUnit check; use appropriate code
Too big for a comment
i have one provider extern that send me that xml for test
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Header>
<AVCabeza transactionID="000032" xmlns="http://webservices.patito/Core/">
<Solicitor entityID="WEST" systemType="WEB" />
</AVCabeza>
</soap:Header>
<soap:Body>
<Availability xmlns:a="http://webservices.patito/Availability/"
xmlns:hc="http://webservices.patito/Common/" summaryOnly="true"
xmlns="http://webservices.patito/og/Availability.wsdl">
<a:AvailabilityDetail availReqType="Room">
<a:Estadia>
<hc:StartDate>2009-01-05T00:00:00.0000000-05:00</hc:StartDate>
<hc:EndDate>2009-01-06T00:00:00.0000000-05:00</hc:EndDate>
</a:Estadia>
<a:HotelSearchCriteria>
<a:HotelRef chainCode="WC"/>
</a:HotelSearchCriteria>
</a:AvailabilityDetail>
</Availability>
</soap:Body>
</soap:Envelope>
I want deserializer so i did it
1) i used the xsd for generate c# class
2) Create a new project class library with the class generate.
The structure
WebServicesExterns (Project)
--> Services (Folder)
---> all class
example
namespace WebServicesExterns.Services
<System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "2.0.50727.42"), _
System.SerializableAttribute(), _
System.Diagnostics.DebuggerStepThroughAttribute(), _
System.ComponentModel.DesignerCategoryAttribute("code"), _
System.Xml.Serialization.XmlTypeAttribute([Namespace]:="http://webservices.patito/Availability.wsdl")> _
Partial Public Class Availability
'''<comentarios/>
<System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "2.0.50727.42"), _
System.SerializableAttribute(), _
System.Diagnostics.DebuggerStepThroughAttribute(), _
System.ComponentModel.DesignerCategoryAttribute("code"), _
System.Xml.Serialization.XmlTypeAttribute([Namespace]:="http://webservices.patito/Core/"), _
System.Xml.Serialization.XmlRootAttribute([Namespace]:="http://webservices.patito/Core/", IsNullable:=false)> _
Partial Public Class AVCabeza
3) After create a test class for try deserializer
using WebServicesExterns.Services;
using System;
using System.Collections;
using System.IO;
using System.Linq;
using System.Runtime.Remoting;
using System.Runtime.Serialization;
using System.Xml;
using System.Xml.Serialization;
using NUnit.Framework;
using System.Reflection;
using System.Runtime.Serialization.Formatters.Soap;
[Test()]
public void ShouldDeserializerSoapMessage()
{
var message = SoapToFromFile(#"C:\rq\Availability.xml");
Assert.IsNotNull(message);
}
public object SoapToFromFile(string filePath)
{
IFormatter formatter;
FileStream fileStream = null;
Object objectFromSoap = null;
try
{
fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
formatter = new SoapFormatter();
objectFromSoap = formatter.Deserialize(fileStream);
}
catch (Exception exception)
{
throw exception;
}
finally
{
if (fileStream != null) fileStream.Close();
}
return objectFromSoap;
}
So, return that error
Parse Error, no assembly associated with Xml key "AVCabeza" "_P1"
Debugging i founded what _P1 is equal to "http://webservices.patito/Core/"
Apparently not found "Type" AVCabeza class
What's wrong?
New update
Look that was the provider give me
one folder with that structure
root
|
-- WSDL_XSD
| |-XSD
| | files with extension .xsd
| |
| --WS
| files with extension .wsdl
|-- XMLSamples
|-files with xml extension that contain soap messsages
well i delete of the xml (previous example) the head
<?xml version="1.0" encoding="utf-8"?>
<Availability xmlns:a="http://webservices.patito/Availability/"
xmlns:hc="http://webservices.patito/Common/" summaryOnly="true"
xmlns="http://webservices.patito/og/Availability.wsdl">
<a:AvailabilityDetail availReqType="Room">
<a:Estadia>
<hc:StartDate>2009-01-05T00:00:00.0000000-05:00</hc:StartDate>
<hc:EndDate>2009-01-06T00:00:00.0000000-05:00</hc:EndDate>
</a:Estadia>
<a:HotelSearchCriteria>
<a:HotelRef chainCode="WC"/>
</a:HotelSearchCriteria>
</a:AvailabilityDetail>
</Availability>
and will try get deserializable availability object but fail this mark
not expected "<Availability .."
now in wsdl_xsd -> ws -> ws i see the availability exists so i think what availability is wrap to availabilitydetail (real request) which is the object i cant delete
availability tag because it have namespace spacefications for childs tags
what think about it?
maybe if i delete availability and insert namespaces (somehow) i could get my de-serializer object
SoapFormatter is not the XML Serializer. You should use the XmlSerializer class.
Also, this is an entire SOAP Message. You'd be better off having them give you the WSDL and using "Add Service Reference". This would give you classes that will do the serialization and deserialization for you.
When you use xsd.exe to generate C# classes from that XML document, in the first step you will get 5 individual .xsd files, providing the
XSD Schema "inferred" from the message. (There are numerous XSD files because you're using numerous XML namespaces in that message)
In particular, XSD.exe will generate code to describe the SOAP Envelope, including the body and header. This is probably not something you want or need to be doing, but the xsd.exe tool infers types for the entire XML document.
Also, the inference engine within xsd.exe is
imprecise. For example, the "StartDate" and "EndDate" child elements of the Estadia element appear to be dates. But xsd.exe won't make that assumption; it will generate an XML schema that marks those things as strings. There are other similar assumptions that xsd.exe makes as it infers. In all cases you probably want to modify the generated xsd files, to match what you truly expect. In the case of StartDate and EndDate, you want to modify the type from xs:string to xs:dateTime.
At that point you can run xsd.exe again, on the .xsd files, using the /c switch, to generate .cs source code. Compile that to get classes that can be used in serialization.
To de-serialize using that generated code, you'd do something like this:
XmlSerializer s1 = new XmlSerializer(typeof(Carlos.Envelope));
Envelope envelope = null;
using(var reader= System.IO.File.OpenText("SampleMessage.xml"))
{
envelope = (Envelope) s1.Deserialize(reader);
}
Then you can open up that Envelope object and get at the various data within it.
Stepping back, you can see that you probably shouldn't be doing this. It's useful and handy to see a sample message, to show you what things ought to look like on the wire. But when generating code to handle classes that serialize to those messages, it's better to start with the source XSD - which is probably available at the service end. This is what John Saunders said in his reply.
If your people can produce a sample message, they probably have the XSD (or the WSDL, which is equivalent) for that message. This would eliminate the requirement for you to infer the xsd, and then modify it to change the broken assumptions, via the inexact process I described above.
The only time you'd need to do that is if you somehow lost the original XSD/WSDL, and you needed to regenerate it.
My application have a configuration xml-file. That file contains more than 50 program settings. At the present time I read and save each program setting separately. I guess It is not effiсiently for such tasks.
I need something that can auto-generate a code for load and save my program settings using predefined xml-schema.
I found a dataset in Add New Item dialog. Unfortunately, i cannot add new code to dataset1 such as events in set-accessors of properties because of this
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
Maybe, there is a tool that allows a user to generate a wrapper for accesing a xml-file ? Such as DataSet1, but with availability to add events.
Edit: I didn't mark a useful answer because i read an articles (link) which you give me. I will mark useful answer later.
If you are not willing to use app.config/web.config or the properties file (which Oded and Bruno recommend and I recommend as well), I highly recommend this utility:
Web Services Contract First (WSCF) Blue for VS2008 and VS2010
If you're on VS2005, you'll want this version of the tool: http://www.thinktecture.com/resourcearchive/tools-and-software/wscf (don't use the VS2008 version on this site. I could never get it to work right.)
Once you have the plugin installed into Visual Studio, you'll need an XSD schema of your XML file. (Google for an online XSD Generator.) Following the instructions found on the WSCF website, you can generate a wrapper class that will deserialize and reserialize your XML and give you an abstracted view of your XML.
I figure it is impossible (or at least very hard) to add new node/element TYPES, but adding new instances of existing node/element types, accessing to your data, editing the data, reordering nodes, and then saving back out are all easy.
Deserialization code looks like this:
private MyGeneratedXMLconfigClass config;
using (StreamReader sr = new StreamReader(filename))
{
XmlSerializer cXml = new XmlSerializer(typeof(MyGeneratedXMLconfigClass));
config = (MyGeneratedXMLconfigClass)cXml.Deserialize(sr);
}
Now your XML has been de-serialized into the "config" instance of your custom class. Then you can access the whole class as a series of nested values and Lists.
For example:
string errorFile = config.errorsFile;
List<string> actions = config.actionList;
var specialActions = from action in config.actionList
where action.contains("special")
select action;
Etc., etc. Then once you're done manipulating your data, you can re-serialize with this code:
using (StreamWriter wr = new StreamWriter(filename, false))
{
XmlSerializer cXml = new XmlSerializer(typeof(MyGeneratedXMLconfigClass));
cXml.Serialize(wr, config);
}
One of the very nice things about this tool is that it auto-generates all classes as "partial" classes, so that you can feel free to extend each class on your own without fear of your code getting stomped on in case you ever need to re-generate because the XSD/XML was changed.
I imagine this might sound like a lot, but the learning curve is actually pretty easy and once you get it installed and working, you'll realize how stupidly easy it is. It's worth it. I swear. :-)
If you have an appropriate xsd schema for your xml file microsoft provides xsd.exe, a small tool which auto-generates c# classes for this schema.
For details see: http://msdn.microsoft.com/en-us/library/x6c1kb0s%28VS.71%29.aspx
Why are you using hand rolled XML for configuration? What is wrong with the existing app.config and web.config schemas?
Why not use a .Settings file?
You can follow these steps:
1) generate an XSD file from your XML file. For There used to be a tool to infer schema from an XML file, I forgot what it's called. Currently I use my own utility, which basically runs this core routine to read an xml file and generate the corresponding xsd:
static void InferSchema(string fileName)
{
XmlWriter writer = null;
XmlSchemaInference infer = new XmlSchemaInference();
XmlSchemaSet sc = new XmlSchemaSet();
string outputXsd = fileName.Replace(".xml", ".xsd");
sc = infer.InferSchema(new XmlTextReader(fileName));
using (writer = XmlWriter.Create(new StreamWriter(outputXsd)))
{
foreach(XmlSchema schema in sc.Schemas())
{
schema.Write(writer);
Console.WriteLine(">> found schema - generated to {0}",
outputXsd);
}
}
}
2) run xsd.exe to generate a serializable class from the XSD file.
xsd.exe /c /n:MyNameSpaceHere MyGenerated.xsd
Next, you can read the XML file into the serializable class using XmlSerializer.Serialize() method. Something like this:
public static void Serialize<T>(T data, TextWriter writer)
{
try
{
XmlSerializer xs = new XmlSerializer(typeof(T));
xs.Serialize(writer, data);
}
catch (Exception e)
{
throw;
}
}
Finally, you can write back to the XML file from the class using the XmlSerializer.Deserialize() method, like this for instance:
public static void Deserialize<T>(out T data, XmlReader reader)
{
try
{
XmlSerializer xs = new XmlSerializer(typeof(T));
data = (T)xs.Deserialize(reader);
}
catch (Exception e)
{
reader.Close();
throw;
}
}
This is called a properties file. C# should have something similar to Java's Properties class where you can load all properties without hard-coding their names.
EDIT:
There's apparently no built-in properties parsing solution for C#. But you can easily implement your own. See here.
If you have an XSD file, you can generate classes from that. Besides the already mentioned xsd.exe from Microsoft (which hasn't been updated for quite some time), there are other tools for this. I am using XSD2Code, which allows generating strongly typed collections, lazy initialization, etc.
If you do not have an XSD, you can point the xsd.exe at your xml-file, and it will generate an XSD from that. The schema usually needs some work, but generally is a good starting point.
xsd.exe (instance).xml
You can use System.Xml.Serialization - it's very easy and you can serialize even class objects directly, like MyCustomClass (it even saves MyCustomClass public fields).
Deserializing the XML file will get a new instance of MyCustomClass, so such a feature is priceless.
Note one thing: you should add EVERY SINGLE TYPE you use in the class, but that's easy though.
I attached a complete project that does the thing you want. just change classes and objects and that will be all.
source code
for example (i'm shortening the code):
using System.Xml;
using System.Xml.Serialization;
using System.IO;
[XmlRootAttribute("Vendor")]
class Vendor{
[XmlAttribute]
Product prod;
}
[XmlRootAttribute("Product")]
class Product{
[XmlAttribute]
public string name="";
}
class Test{
Vendor v=new Vendor();
Product p=new Product();
p.name="a cake";
v.prod=p;
//add EVERY SINGLE TYPE you use in the serialized class.
Type[] type_list = { typeof(Product) };
XmlSerializer packer = new XmlSerializer(v.GetType(),type_list);
XmlWriter flusher = XmlWriter.Create(#"c:\bak.xml");
packer.Serialize(flusher, v);
flusher.Close();
XmlReader restorer = XmlReader.Create(#"c:\bak.xml");
Vendor v2 = (Vendor)packer.Deserialize(restorer);
//v2.prod.name is now "cake"
//COOL was my first impression :P
}
I have a rather detailed xml file. Below is the top level nodes (I have included the ellipse as the lower level nodes are all well formed and properly filled with data):
<?xml version="1.0" encoding="UTF-8"?>
<config>
<Models>...</Models>
<Data>...</Data>
</config>
I have created an xsd file from using the Visual Studio 2008 command prompt:
xsd sample.xml
This generates the xsd file just fine. I then auto generate classes from the xsd with the command:
xsd sample.xsd /classes
For the deserialization of the xml file into a class object, I'm using the read function in the helper class:
public class XmlSerializerHelper<T>
{
public Type _type;
public XmlSerializerHelper()
{
_type = typeof(T);
}
public void Save(string path, object obj)
{
using (TextWriter textWriter = new StreamWriter(path))
{
XmlSerializer serializer = new XmlSerializer(_type);
serializer.Serialize(textWriter, obj);
}
}
public T Read(string path)
{
T result;
using (TextReader textReader = new StreamReader(path))
{
XmlSerializer deserializer = new XmlSerializer(_type);
result = (T)deserializer.Deserialize(textReader);
}
return result;
}
}
When attempting the deserialization with:
var helper = new XmlSerializerHelper<configModels>();
var obj = new configModels();
obj = helper.Read(filepath);
I receive an error that I have deduced is because the deserializer is looking for the 'Models' node but the corresponding class name was generated as a combination of the root node and the 'Model' node (configModels). Why are the class names generated like this?
I tried to deserialize from the top node using:
var helper = new XmlSerializerHelper<config>();
var obj = new config();
obj = helper.Read(filepath);
Unfortunately, this the results in a slew of errors like the following:
System.InvalidOperationException was unhandled by user code
Message="Unable to generate a temporary class (result=1).
error CS0030: Cannot convert type 'Application.Lease[]' to 'Application.Lease'
error CS0030: Cannot convert type 'Application.CashFlow[]' to 'Application.CashFlow'
...ect.
Can somebody steer me towards what I might be doing wrong with my xsd auto-generating?
XSD.EXE is a good start - but it's far from perfect. Also, based on the XML you provided, XSD.EXE can't always decide for sure whether something is a single instance of an object, or an open-ended array of objects.
This seems to be the case for your two elements - Application.Lease and Application.CashFlow. How are they defined in the generated XSD file? Does that make sense to you? Quite possibly, you'd have to add a little hints, such as:
<xs:element name="Lease" minOccurs="0" maxOccurs="1" />
for an optional property, that's zero or one occurences only. Things like that are really hard for the xsd.exe tool to figure out based on just a single XML sample file.
Marc
Go to your generated class and change all from [][] ---> []
There's an issue with xsd.exe and lists. You have to go into the generated class and manually edit the file to the correct type. I've switched to using Xsd2Code. So far it doesn't seem to have this problem.
Another issue that can cause this problem is that the xml file contents between the tags (meaning the content) is still encoded when it shouldn't be. For example, the <br> tags in my content were still <br> instead of <br />. The xsd generator turned these into elements in the schema then mislabeled them as unbounded since there was more than one found. Unencoding them fixed the problem and generated the classes correctly.