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);
Related
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>
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.
I'm fairly new to XML and C#, so please understand if this question is too sily to ask.
I'm converting XML format using C# win-form application.
The app opens a xml file using 'OpenFileDialog', then conversion will be excuted(this is already done, but I still need to add or remove some more like below) .
After conversion, the app will save the modified xml file using 'SaveFileDialog'.
Original XML Format
<?xml version="1.0" encoding="utf-8" ?>
<DataList>
<Data>
<ID>1</ID>
<Name>Mike</Name>
<Age>23</Age>
</Data>
<Data>
<ID>1</ID>
<Name>Mike</Name>
<Age>23</Age>
</Data>
<Data>
<ID>1</ID>
<Name>Mike</Name>
<Age>23</Age>
</Data>
..<Data></Data> continued...
</DataList>
I want to edit the XML file as below
<?xml version="1.0" encoding="utf-8" ?> **→ Remove this delaration!**
<MainInterface> **→ Add 'root element' before existing nodes**
<DataList>
<Data>
<ID>1</ID>
<Name>Mike</Name>
<Age>23</Age>
</Data>
<Data>
<ID>1</ID>
<Name>Mike</Name>
<Age>23</Age>
</Data>
<Data>
<ID>1</ID>
<Name>Mike</Name>
<Age>23</Age>
</Data>
..<Data></Data> continued...
</DataList>
</MainInterface> **→ close newly added root element**
I've tried below code but seem like it doesn't work
OpenFileDialog openFileDialogue = new OpenFileDialog();
openFileDialog1.DefaultExt = "xml";
openFileDialog1.Filter = "xml files (*.xml)|*.xml";
openFileDialog1.Title = "Select a xml File";
openFileDialog1.ShowDialog();
XDocument xmlFile = XDocument.Load(openFileDialog1.FileName);
**// Remove Declaration**
XDocument doc = new XDocument(new XDeclaration(null, null, null));
**// Add Root Element**
XElement doc1 = XElement.Parse(openFileDialog1.FileName);
XElement root = new XElement("MainInterface", doc1);
//doc.Save(_data)
openFileDialog1.FileName = root.ToString();
-----------------------------------------------------------------------------------
Do something for conversion ~~~
-----------------------------------------------------------------------------------
SaveFileDialog saveFileDialogue1 = new SaveFileDialog();
saveFileDialog1.Filter = "xml File |*.xml";
saveFileDialog1.Title = "Conversion Completed! Save a XML file";
saveFileDialog1.FileName = "XML Converted.xml";
saveFileDialog1.ShowDialog();
xmlFile.Save(saveFileDialog1.FileName);
Should I use XML Writer? Is there simpler way of removing declaration and adding root element on the existing xml file? Thank you in advance.
Is that your XML structure? or is it bound to change?
See my way to parse this:
var xDoc = XDocument.Load(openFileDialog1.FileName);
//Use code below if you'll use string to Load XDocument
/*var xmlString = #"<?xml version=""1.0"" encoding=""utf-8"" ?>
<DataList>
<Data>
<ID>1</ID>
<Name>Mike</Name>
<Age>23</Age>
</Data>
<Data>
<ID>1</ID>
<Name>Mike</Name>
<Age>23</Age>
</Data>
<Data>
<ID>1</ID>
<Name>Mike</Name>
<Age>23</Age>
</Data>
<Data></Data> continued...
</DataList>
";
var xDoc = XDocument.Parse(xmlString);*/
var dataList = xDoc.Descendants(#"Data");
var newXDoc = new XDocument(new XDeclaration(null, null, null),
new XElement("MainInterface",
new XElement("DataList",
dataList.Select(data =>
new XElement("Data",
data.Element("ID"),
data.Element("Name"),
data.Element("Age")
)
)
)
)
);
See this image link for my XML Dump with LINQPad.
Just get xml as string and play with it! To remove header you can use Replace function! To add root element just add <MainInterface> to the begining and </MainInterface> to the end of xml string.
To convert to string you can use XDocument.ToString()
Using some example code I found here on SO, I've put together a bit of code to open up an existing XML database, open up another XML file that's to be inserted into the first XML, get the descendants of the root node in that file, and append them to the base of the selected node in the database.xml file, and then save the resultant file to a new XML file. The code below compiles & runs with no errors in VS2010, but doesn't add the XML from the actionID.xml file to the database.xml file and I don't know what I'm missing.
Here's the C# code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
class pcbdbUpdate
{
static void Main( )
{
//open database & load into memory
XDocument PCBDB = XDocument.Load("C:\\database.xml");
//open Activity Log file & load into memory
XDocument addAction = XDocument.Load("C:\\actionID.xml");
//get descendant node(s) below PCBDBActivityLog root node
var actionXML = addAction.Root.Descendants("PCBDBActivityLog");
/*supposed to write out child nodes, but not very useful, just tells me
it's a: System.Xml.Linq.XContainer+<GetDescendants>d__a*/
Console.WriteLine(actionXML.ToString());
//enumerate database for SBE_PCB_Data nodes
IEnumerable<XElement> SBE_PCB_Data = PCBDB.Element("PCBDatabase").Elements("SBE_PCB_Data");
//look for specific node with PCBID of "00001" and select it
XElement newAction = SBE_PCB_Data.Where(p => p.Attribute("PCBID").Value == "00001").FirstOrDefault();
//write descendants from Activity Log to base of node
newAction.Add(actionXML);
//save the resultant file
PCBDB.Save("C:\\newDatabase.xml");
}
}
Here's the database.xml:
<?xml version="1.0" encoding="utf-8"?>
<PCBDatabase>
<SBE_PCB_Data PCBID="00001">
<Creation ActionID="0002" User="DELLIOTTG:192.168.1.69" Date="2012-10-31T14:35:58" PCBID="00001">
<PCBDrawing>00001a</PCBDrawing>
<AssemblyDrawing>00001b</AssemblyDrawing>
<Vendor>SBE</Vendor>
<PONumber>00000</PONumber>
</Creation>
<Assignment ActionID="1295" User="RHO:192.168.1.6" Date="2012-12-13T08:59:31" PCBID="00001">
<PCBDrawing>00002a</PCBDrawing>
<AssemblyDrawing>00001c</AssemblyDrawing>
<Vendor>SBE</Vendor>
<PONumber>00001</PONumber>
</Assignment>
</SBE_PCB_Data>
<SBE_PCB_Data PCBID="00002">
<Assignment ActionID="630c" User="DMUELLER:192.168.1.152" Date="2010-03-15T13:14:21" PCBID="00002">
<SBEJobNumber>57380</SBEJobNumber>
</Assignment>
</SBE_PCB_Data>
</PCBDatabase>
And here's the actionID.xml:
<?xml version="1.0"?>
<PCBDBActivityLog>
<Assignment ActionID='8353' User='DMUELLER:192.168.1.134' Date='2011-01-27T15:38:25' PCBID='00001'>
<SBEPN>41528E</SBEPN>
</Assignment>
</PCBDBActivityLog>
The resultant file should look like this:
<?xml version="1.0" encoding="utf-8"?>
<PCBDatabase>
<SBE_PCB_Data PCBID="00001">
<Creation ActionID="0002" User="DELLIOTTG:192.168.1.69" Date="2012-10-31T14:35:58" PCBID="00001">
<PCBDrawing>00001a</PCBDrawing>
<AssemblyDrawing>00001b</AssemblyDrawing>
<Vendor>SBE</Vendor>
<PONumber>00000</PONumber>
</Creation>
<Assignment ActionID="1295" User="RHO:192.168.1.6" Date="2012-12-13T08:59:31" PCBID="00001">
<PCBDrawing>00002a</PCBDrawing>
<AssemblyDrawing>00001c</AssemblyDrawing>
<Vendor>SBE</Vendor>
<PONumber>00001</PONumber>
</Assignment>
<Assignment ActionID='8353' User='DMUELLER:192.168.1.134' Date='2011-01-27T15:38:25' PCBID='00001'>
<SBEPN>41528E</SBEPN>
</Assignment>
</SBE_PCB_Data>
<SBE_PCB_Data PCBID="00002">
<Assignment ActionID="630c" User="DMUELLER:192.168.1.152" Date="2010-03-15T13:14:21" PCBID="00002">
<SBEJobNumber>57380</SBEJobNumber>
</Assignment>
</SBE_PCB_Data>
</PCBDatabase>
What am I missing or doing wrong?
Error is here:
var actionXML = addAction.Root.Descendants("PCBDBActivityLog")`
You are trying to find <PCBDBActivityLog> elements under the document root. But this will return nothing, because <PCBDBActivityLog> element is a root of actionID.xml document. Replace this line with
var actionXML = addAction.Descendants("PCBDBActivityLog");
Or
var actionXML = addAction.Root;
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