I'm wanting to offer a way to save work in an application. It is a sort of project functionality. The projects need to be serialized to XML. I've got DataContractSerializer generating the XML, but I need to be able to select a location in an XML file for it to serialize to instead of writing a complete file for itself since there will be multiple projects and I will need to be able to de/serialize these projects individually from a Projects XML file.
What I would like for XML:
<Projects>
<Project>
<ID>...</ID>
<Name>...</Name>
<LastEdited>...</LastEdited>
...
</Project>
<Project>...</Project>
<Project>...</Project>
</Projects>
I would then serialize and deserialize based on the Project ID using Linq to XML to navigate the doc.
Any suggestions? Much thanks in advance.
Found a way to do this. It has its hitches but once you get everything included it works pretty smoothly.
To generate the XML:
DataContractSerializer ser = new DataContractSerializer(typeof(Project));
StringBuilder objectSB = new StringBuilder();
XmlWriter objectWriter = XmlWriter.Create(objectSB);
//I'm using this method in the object I want to serialize (hence the 'this')
ser.WriteObject(objectWriter, this);
objectWriter.Flush();
Then you can turn the StringBuilder into an XElement for Linq to XML with:
Xelement xProject = Xelement.Parse(objectSB.ToString());
For deserialization:
DataContractSerializer ser = new DataContractSerializer(typeof(Project));
Project project = (CoilProject)ser.ReadObject(xProject.CreateReader());
There are a few times when I edit the XML using Linq to XML without deserializing and reserializing. Due to the namespaces that the DataContractSerializer adds, this becomes necessary:
//place this somewhere in your project's namespace
public static class MyExtensions
{
public static IEnumerable<XElement> ElementsAnyNS(this XElement source, string localName)
{
return source.Elements().Where(e => e.Name.LocalName == localName);
}
public static XElement ElementAnyNS(this XElement source, string localName)
{
return source.Elements().Where(e => e.Name.LocalName == localName).Select(e => e).Single();
}
}
Those extension methods allow you to select one or many elements from the serialized object without worrying about namespace. I adapted these extension methods from Pavel Minaev's answer on Ignore namespaces in LINQ to XML.
Related
Is it possible to add literal XML data within a C# code file? I'm currently using a multiline string literal but it gets messy as you can see. Any better way of doing this?
string XML = #"<?xml version=""1.0"" encoding=""utf-8""?>
<customUI xmlns=""http://schemas.example.com/customui"">
<toolbar id=""save"">
</toolbar>
</customUI>";
XML literals are a feature of VB.NET, not C#.
What you have posted is as close as you can get in C#.
You may want to consider replacing the embedded double quotes with single quotes though (as both types are valid XML).
For larger amounts of XML you may want to consider the answer from Marc - using an XML file (loaded once and stored in memory), so you can take advantage of the XML editor.
If the XML is big enough to get in the way, consider using a flat .xml file instead, either loaded from disk, or embedded as a resource. As long as you only load it once (perhaps in a static constructor) this will make no difference to performance. It will be considerably easier to maintain, as it will use the IDE's XML file editor. And it won't get in the way of your code.
With reference to my comment, I couldn't recall where I saw this, but I finally found the XmlBuilder link.
In retrospect, it seems Linq to XML would be your best bet. It's cleaner, faster and more maintainable than concatenating XML strings:
XNamespace ns = "http://schemas.example.com/customui";
XDocument doc = new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XElement(ns + "customUI",
new XElement(ns + "taskbar",
new XAttribute("id", "save"))
)
);
var stringWriter = new StringWriter();
doc.Save(stringWriter); //Write to StringWriter, preserving the declaration (<?xml version="1.0" encoding="utf-16" standalone="yes"?>)
var xmlString = stringWriter.ToString(); //Save as string
doc.Save(#"d:\out.xml"); //Save to file
As a peculiar, and very case-specific solution, if you happen to be working in an ASP.NET environment using the Razor engine, in a CSHTML file you can:
Func<MyType, HelperResult> xml = #<root>
<item>#(item.PropertyA)</item>
<item>#(item.PropertyB)</item>
<item>#(item.PropertyC)</item>
</root>;
With the addition of an extension method:
public static XDocument ToXDocument<T>(this Func<T, HelperResult> source, T item)
{
return XDocument.Parse(source(item).ToHtmlString());
}
You can then:
XDocument document = xml.ToXDocument(new MyType() {
PropertyA = "foo",
PropertyB = "bar",
PropertyC = "qux",
});
Again, peculiar? Yes. Case-specific? Yes. But it works, and gives great Intellisense. (mind you, it also will give a bunch of validity warnings, depending on the document validation version)
The closest we could have in C# would be through LINQ, something like that:
var xml = XDocument.Load(
new StringReader(#"<Books>
<Book author='Dan Brown'>The Da Vinci Code</Book>
<Book author='Dan Brown'>The Lost Symbol</Book>
</Books>"));
var query = from book in xml.Elements("Books").Elements("Book")
where book.Attribute("author").Value == "Dan Brown"
select book.Value;
foreach (var item in query) Console.WriteLine(item);
I am using XmlSerializer in c# to serialize a class instance to xml. By default, if I serialize the instance of following class:
public class XmlTest
{
public bool b1;
}
the bool field is represented as <b1>false</b1>
I have specific requirement that the value of bool fields be represented as elements as in <false> and <true> instead of text content. I have similar requirements for enum fields as well.
I have tried using attributes that control XML serialization but couldn't find one that meets my requirement. I am fairly new to c# and was wondering the best way to achieve this.
You can't do that with the XML Serializer. You can use LINQ to XML instead:
public XElement Serialize(XmlTest t){
return new XElement("XmlTest",
new XElement(t.b1 ? "true" : "false"));
}
One hint on using LINQ to XML in an code generation setting: XElement has a constructor that looks very simple:
public XElement(
XName name,
Object content
)
but that content parameter can be almost anything. That includes
Any type that implements ToString
Any type that implements IEnumerable
So, for example:
XElement xmlTree2 = new XElement("Root",
from el in xmlTree1.Elements()
where((int)el >= 3 && (int)el <= 5)
select el
);
could produce
<Root>
<Child3>3</Child3>
<Child4>4</Child4>
<Child5>5</Child5>
</Root>
I'm using
List<EFacebook> facebooks = BFacebook.ReadFacebookFriends(user.EProviders
.Where(i => i.ProviderType == EProvider.EnumProviderType.Facebook)
.First().Token);
StringBuilder xml = new StringBuilder();
foreach (EFacebook i in facebooks)
{
xml.AppendFormat("<id>{0}</id>", i.id);
}
Can anybody suggest a better code to serialize each i.id into an XML string?
Edit:
The facebook object has close to 20 properties. If I XmlSerializer all 20 properties are serialized into the XML. I just need the id column.
You might want to take a look at XML Serialization already built into the .NET Framework.
You can use code similar to the following to serialize the object:
MySerializableClass myObject = new MySerializableClass();
// Insert code to set properties and fields of the object.
XmlSerializer mySerializer = new
XmlSerializer(typeof(MySerializableClass));
// To write to a file, create a StreamWriter object.
StreamWriter myWriter = new StreamWriter("myFileName.xml");
mySerializer.Serialize(myWriter, myObject);
myWriter.Close();
See: How to serialize
You can flag properties to be ignored using the XmlIgnore attribute as shown below:
public class Group
{
// The XmlSerializer ignores this field.
[XmlIgnore]
public string Comment;
// The XmlSerializer serializes this field.
public string GroupName;
}
See XMLIgnore
XmlSerializer xs = new XmlSerializer(typeof(int[]));
xs.Serialize(stream,facebooks.Select(x=>x.id).ToArray())
I would use Linq To Xml to create the Xml Tree structure
See for an example: Linq To Xml
If you're just saving the id values into an xml string then I wouldn't say that you're 'serializing' the objects.
Whenever working with XML it's much nicer to use an XML library than to wrangle with text. One obvious reason is that it guarantees your XML will be well-formed.
I'd do something like this:
List<EFacebook> facebooks = GetFriends();
var facebookIds = facebooks.Select(f => new XElement("id", f.id));
var facebookXml = new XElement("facebookIds", facebookIds);
Which will give you XML like
<facebookIds>
<id>1</id>
<id>2</id>
</facebookIds>
I have the following XML structure
<T>
<F>
<H>
<H1>some value</H1>
<H2>some value</H2>
<H3>some value</H3>
</H>
<O>
<P>some value</P>
<TI>some value</TI>
<TI>some value</TI>
</O>
<R>
<PTY>some value</PTY>
<PTY>some value</PTY>
<PTY>some value</PTY>
</R>
</F>
<T>
I need to parse this xml in C# and get the values out of them to be further exported to a CSV file.
My query is how do you go about creating an entity for this XML
You can play with XmlSerializer and its related attributes.
As long as the XML is not too complex, there's not much work to do.
To read the XML:
var serializer = new XmlSerializer(typeof(SerializableObject));
SerializableObject deserialized;
using (var stream = new FileStream(#"C:\test.xml", FileMode.Open))
{
deserialized = (SerializableObject)serializer.Deserialize(stream);
}
The SerializableObject will look like this:
[Serializable]
[XmlRootAttribute("T")]
public class SerializableObject
{
...
}
BONUS for lazy programmers: You can just use Xsd.exe to brute force create an object from an XML file. Then tweak the results to your needs.
You can use LinqToXml to parse xml. StringBuilder will be helpful to produce CSV.
I think these How Tos will be useful. They describe all you needed to parse this xml.
add using System.Xml.Linq;
then you can do something similar to this:
XDocument xml = XDocument.Load(#"....\People.xml");
var query = from p in xml.Elements("people").Elements("person")
where (int)p.Element("id") == 1
select p;
I currently load an XML file into a list objects using code like this
XDocument xmlDoc = XDocument.Load(path);
List<ImportDefinition> importDefinitions = xmlDoc.Descendants("Root").Select(xElem => (ImportDefinition)xElem).ToList();
return importDefinitions;
This list of objects contains nested objects and each one has an operator for parsing the XML into the correct form like this
public static explicit operator Rules(XElement xElem)
{
try
{
return new Rules()
{
FileNameRegEx = (string)xElem.Element("FileNameRegEx"),
FileExtension = (string)xElem.Element("FileExtension")
};
}
catch (Exception ex)
{
return null;
}
This works fine for loading the XML. I now want to save this list of objects back to XML after some edits have been made.
I was hoping something like this would work
XElement xml = new XElement("Root",
from p in ObjectList
select new XElement("File",RootObject
));
}
xml.Save("C:\\temp\\newimport.xml");
However this just seems to output this
<?xml version="1.0" encoding="utf-8"?>
<Root>
<File>MyNamespace.RootObject</File>
<File>MyNamespace.RootObject</File>
</Root>
It looks like its not using the custom operators it uses when loading the files to work out the format to save in. Whats the best way to save this data back to XML to the same format it was in when I read it?
Well for one thing you've only shown us the operator for parsing from an XElement... but even so, you're obviously explicitly calling that in your LINQ expression. If you want the equivalent when building XML, you'll need to be explicit there too:
XElement xml = new XElement("Root",
from p in ObjectList
select new XElement("File", (XElement) p));
Personally I'd use methods instead of operators - ToXElement() and FromXElement() - I think it's clearer that way. ToXElement would be an instance method; FromXElement would be a static method. This is a pattern I've used many times, and it's always worked fine.