I have an xml file
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfLocations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Location xsi:type="JointLocation">
<Name>Example Location</Name>
<Index>0</Index>
<ZClearance>0</ZClearance>
<Joint1>100</Joint1>
<Joint2>200</Joint2>
<Joint3>200</Joint3>
<Joint4>200</Joint4>
<Joint5>200</Joint5>
<joint6>0</joint6>
<Joint6>0</Joint6>
</Location>
</ArrayOfLocations>
I load this file into a data set, and then into a DataGridView. From that DataGridView
I can add new Location elements, or edit existing Location elements and save. When I save, I am doing this
string path = filePathBox.Text;
DataSet ds = (DataSet)dataGridView1.DataSource;
ds.WriteXml(filePathBox.Text);
After saving, the XML file then looks like
<?xml version="1.0" standalone="yes"?>
<ArrayOfLocations>
<Location>
<Name>Example Location</Name>
<Index>0</Index>
<ZClearance>0</ZClearance>
<Joint1>100</Joint1>
<Joint2>200</Joint2>
<Joint3>200</Joint3>
<Joint4>200</Joint4>
<Joint5>200</Joint5>
<joint6>0</joint6>
<Joint6>0</Joint6>
</Location>
</ArrayOfLocations>
As you can see the xsi and namespace have been removed. I would like to preserve these attributes.
So far I have tried adding as an additional parameter to WriteXML():
ds.WriteXML(filepath, XmlWriteMode.WriteSchema)
However, this creates a big mess and still does not maintain the initial format that I want to preserve. Any tips?
A simple example shows us that ReadXML/WriteXML will lose the schema info you're interested in
using (FileStream fs = new FileStream("D:\\Workspace\\FormTest\\input.xml", FileMode.Open))
{
DataSet ds = new DataSet();
ds.ReadXml(fs);
ds.WriteXml(Console.Out);
}
Gives us
<ArrayOfLocations>
<Location>
<Name>Example Location</Name>
<Index>0</Index>
<ZClearance>0</ZClearance>
<Joint1>100</Joint1>
<Joint2>200</Joint2>
<Joint3>200</Joint3>
<Joint4>200</Joint4>
<Joint5>200</Joint5>
<joint6>0</joint6>
<Joint6>0</Joint6>
</Location>
</ArrayOfLocations>
The best way I found to recover this schema information is to Deserialize the data. It's not pretty but it worked for me:
XmlSerializer s = new XmlSerializer(typeof(ArrayOfLocations));
ArrayOfLocations fix = new ArrayOfLocations();
using (FileStream fs = new FileStream("D:\\Workspace\\FormTest\\input.xml", FileMode.Open))
{
DataSet ds = new DataSet();
ds.ReadXml(fs);
string xml = ds.GetXml();
ArrayOfLocations input = (ArrayOfLocations)s.Deserialize(new StringReader(xml));
foreach(var location in input)
{
fix.Add(new JointLocation()
{
Name = location.Name,
...
Joint6 = location.Joint6
});
}
}
XmlTextWriter xtw = new XmlTextWriter(Console.Out);
xtw.Formatting = Formatting.Indented;
s.Serialize(xtw, fix);
And of course you'll need to create the classes that model your schema in order to deserialize:
public class Location
{
public string Name { get; set; }
...
public byte Joint6 { get; set; }
}
public class JointLocation : Location { }
[XmlInclude(typeof(JointLocation))]
[XmlRoot("ArrayOfLocations")]
public class ArrayOfLocations : List<Location> { }
Note the following gotchas
You need to define a JoinLocation class, specify that type under XmlInclude, and manually fix your object all in order to comply with the schema that you want to output to.
You will need to use a XmlTextWritter to respect indentation
This all should give you the desired output:
<?xml version="1.0" encoding="Codepage - 437"?>
<ArrayOfLocations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Location xsi:type="JointLocation">
<Name>Example Location</Name>
<Index>0</Index>
<ZClearance>0</ZClearance>
<Joint1>100</Joint1>
<Joint2>200</Joint2>
<Joint3>200</Joint3>
<Joint4>200</Joint4>
<Joint5>200</Joint5>
<Joint6>0</Joint6>
</Location>
</ArrayOfLocations>
Related
what is simplest way to merge two XML file and create new one? I'm using unity and i think it is limited in C# APIs ans references and I'm not professional programmer.
File 1:
<?xml version="1.0" encoding="UTF-8"?>
<UserData>
<Data>
<FIELD1>NAME 1</FIELD1>
</Data>
</UserData>
File 2:
<?xml version="1.0" encoding="UTF-8"?>
<UserData>
<Data>
<FIELD2>NAME 2</FIELD2>
</Data>
</UserData>
Result:
<?xml version="1.0" encoding="UTF-8"?>
<UserData>
<Data>
<FIELD1>NAME 1</FIELD1>
<FIELD2>NAME 2</FIELD2>
</Data>
</UserData>
I'd start with making your implementation of UserData more generic, so that it can accept multiple field types instead of field1 and field2, like:
<?xml version="1.0" encoding="UTF-8"?>
<UserData>
<Data>
<Field>NAME 1</Field>
<Field>NAME 2</Field>
</Data>
</UserData>
Your container could then look like this:
[XmlRoot("UserData")]
public class UserDataContainer
{
public UserDataContainer() {...}
// Can be used load and save a list of strings (Field(s) in XML)
[XmlArray("Data"), XmlArrayItem("Field"),Type = typeof(string))]
public List<string> Data = new List<string>();
public static UserDataContainer Load(path){...}
public void Save(path){...}
public List<string> GetFields(){...}
public void SetFields(List<string> Fields){...}
}
If you need help on loading/writing data specifically you can check out this wiki, it's very well done!
Saving and Loading Data, XmlSerializer
You would load your two UserData files like:
UserDataContainer UserData1 = UserDataContainer.Load(UserData1Path);
UserDataContainer UserData2 = UserDataContainer.Load(UserData2Path);
Later when you want to merge them you could do this:
// Get the contents of the first user data
List<string> CombinedDatas = UserData1.GetFields();
// Combine in the contents of the second user data
// AddRange actually changes the first list (CombinedDatas)
CombinedDatas.AddRange(UserData2.GetFields());
// If you wanted to save out the combined data in a new xml file
UserDataContainer FinalData = new UserDataContainer();
FinalData.SetFields(CombinedDatas);
FinalData.Save(FinalDataPath);
When I'm trying to edit XML Element and save it, it generates copy (with edited element) and appends it to end of file.
var localStore = IsolatedStorageFile.GetUserStoreForApplication();
IsolatedStorageFileStream stream = new IsolatedStorageFileStream("DataFolder\\PlayerData.xml", FileMode.OpenOrCreate, FileAccess.ReadWrite, localStore);
var doc = XDocument.Load(stream);
doc.Root.Element("characters").Element("character").SetElementValue("expierence", 10);
doc.Save(stream, SaveOptions.None);
stream.Close();
Example output file:
<?xml version="1.0" encoding="utf-8"?>
<root>
<characters>
<character>
<expierence>0</expierence>
</character>
</characters>
</root><?xml version="1.0" encoding="utf-8"?>
<root>
<characters>
<character>
<expierence>10</expierence>
</character>
</characters>
</root>
That's exactly what you told it to do by passing FileMode.OpenOrCreate.
If you want to truncate any existing file, pass Create.
For more information, see the documentation.
1.What I want
get proper xml elements out of a xml file
The XML
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<tasks>
<task>
<title>Test0001</title>
<due>06/17/2012</due>
</task>
<task>
<title>Test0002</title>
<due>06/17/2012</due>
</task>
<task>
<title>Test0003</title>
<due>06/17/2012</due>
</task>
<tasks>
2.what I code
The Linq block:
StorageFile file1 = await ApplicationData.Current.LocalFolder.GetFileAsync("BetterTask.xml");
XDocument doc1 = new XDocument();
using (var ReadStream1 = await file1.OpenStreamForReadAsync() as Stream)
{
doc1 = XDocument.Load(ReadStream1);
}
var data = from query in doc1.Descendants("task")
select new mytask
{
title = (string)query.Element("title"),
due = (string)query.Element("due")
};
List<mytask> myTasks = data.ToList();
myTodayListBox.ItemsSource = myTasks;
the mytask Class
public class mytask
{
public string title { get; set; }
public string due { get; set; }
}
3.What I get
I set a break at the last of the block, when the app break, i found in the LinQ part:
<1>the [doc1] is full of the XML content
<2>but [myTasks] is empty(count = 0),....
4. Why and how to solve
:(
I think the variable doc1 in your case actually refers the document itself. You will have to do something like:
doc1.Root.Descendants("task")
to get it to work.
I have a generic list of objects that I am trying to serialize/desearilize.
The objects' class itself has a property of a generic list of another class,
class Exercise
{
public int Duration { get; set; };
public string Name { get; set; };
}
class Session
{
public DateTime Date { get; set; }
public List<Exercise> ExerciseList { get; set; }
}
This is how serialization looks like
Session session = new Session((DateTime)dpDate.Value, exercises); //exercises is a List<Exercise>
...
Sessions = new List<Session>();
Sessions.Add(session);
XmlSerializer xml = new XmlSerializer(typeof(List<Session>));
xml.Serialize(stream, Sessions);
...
This is how the resulting xml looks like
<?xml version="1.0" encoding="utf-16"?>
<ArrayOfSession xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Session>
<Date>2012-01-17T00:00:00+03:00</Date>
<ExerciseList>
<Exercise>
<Name>q</Name>
<Duration>10</Duration>
</Exercise>
<Exercise>
<Name>w</Name>
<Duration>20</Duration>
</Exercise>
</ExerciseList>
</Session>
</ArrayOfSession>
However, when trying to deserialize it like so
...
XmlSerializer xml = new XmlSerializer(typeof(List<Session>));
Sessions = (List<Session>)xml.Deserialize(stream);
...
It shows the following error on the second line :
InvalidOperationException There is an error in xml document (3,4)
So what might be the problem?
EDIT:
I need to clarify that it is for windows phone, so the stream is formed like this
IsolatedStorageFileStream stream = storage.CreateFile(fileName);
Sessions = new List<Session>();
Sessions.Add(session);
XmlSerializer xml = new XmlSerializer(typeof(List<Session>));
xml.Serialize(stream, Sessions);
However, for the purpose of showing the xml structure I used
StringWriter s = new StringWriter();
xml.Serialize(s, Sessions);
So, I do not have access to the actual xml file to remove encoding="utf-16"
It won't let me answer my own question due to low reputation for another 5 hours, so I'll post it here:
So, the problem turned out to be not in encoding, as has been suggested.
Apparently, all the classes that are being serialized must have a default parameter less constructor present. Now that I've added them everything works splendidly. (I still do not know if it is a thing with windows phone xml serialization or xml serialization in general)
Try this
//Serialize
public static string SerializeObject<T>(object o)
{
MemoryStream ms = new MemoryStream();
XmlSerializer xs = new XmlSerializer(typeof(T));
XmlTextWriter xtw = new XmlTextWriter(ms, Encoding.UTF32);
xs.Serialize(xtw, o);
ms = (MemoryStream)xtw.BaseStream;
UTF32Encoding encoding = new UTF32Encoding();
return encoding.GetString(ms.ToArray());
}
//Deserialize
public static T DeserializeObject<T>(string xml)
{
XmlSerializer xs = new XmlSerializer(typeof(T));
UTF32Encoding encoding = new UTF32Encoding();
Byte[] byteArray = encoding.GetBytes(xml);
MemoryStream ms = new MemoryStream(byteArray);
XmlTextWriter xtw = new XmlTextWriter(ms, Encoding.UTF32);
return (T)xs.Deserialize(ms);
}
Hope this solves your problem.
If possible go through this post.
You're missing a closing tag for the <ArrayOfSession> element.
<?xml version="1.0" encoding="utf-16"?>
<ArrayOfSession xmlns:xsi= ...">
<Session>
...
</Session>
</ArrayOfSession>
I have following sample xml file
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<IResponse xmlns:xsi="http://www.w3.org/2001/XMLScheminstance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Language>en</Language>
<Code>Approved</Code>
<Message> Approved</Message>
<Info xsi:type="Info">
<Number>11</Number>
<ExpiryDate year="10" month="8" />
<StartDate year="7" month="8" />
<currency="GBP">36.00</currency>
<ACode>096392</ACode>
</IResponse>
How to display the nodes and child elemants in treeview control and values in the list view?
public void Deserialize()
{
XmlReader reader = XmlReader.Create(this.filePath);
XmlSerializer serializer = new XmlSerializer(typeof(Response));
if (serializer.CanDeserialize(reader))
{
Response obj = serializer.Deserialize(reader) as Response;
// obj consists of xml file nodes and i want to display this in treeview
// control and values in between them as list view .
}
else
{
iccTransactionResponseBindingSource.DataSource = null;
}
}
Maybe this MS KB document? http://support.microsoft.com/kb/317597