I have an XML like this
<Root>
<Branch>
<Child Id="0">
<Reference Name="B0"/>
<Details Number="2">
<Detail Height="50"/>
<Detail Weight="3"/>
</Details>
</Child>
<Child Id="2">
<Reference Name="B2"/>
<Details Number="2">
<Detail Height="55"/>
<Detail Weight="3.5"/>
</Details>
</Child>
</Branch>
</Root>
I want to add a new block of data for Child ID=1 after Child ID=0 data block
It is probably easier to add the new child to the current list. Then sort the children. See the xml linq code below
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)
{
XElement newChild = new XElement("Child", new object[] {
new XAttribute("Id", "1"),
new XElement("Reference", new XAttribute("Name", "B1")),
new XElement("Details", new object[] {
new XAttribute("Number", "2"),
new XElement("Detail", new XAttribute("Height","52")),
new XElement("Detail", new XAttribute("Weight","3.25"))
})
});
XDocument doc = XDocument.Load(FILENAME);
XElement branch = doc.Descendants("Branch").FirstOrDefault();
branch.Add(newChild);
List<XElement> orderedChildren = branch.Elements("Child").OrderBy(x => (int)x.Attribute("Id")).ToList();
XElement newBranch = new XElement("Branch", orderedChildren);
branch.ReplaceWith(newBranch);
}
}
}
Related
I am a new user on this forum and I'm not a highly experienced developer/programmer. Before asking this question, I searched and looked through Stack Overflow and some Microsoft suggested links, but I didn't find what I was looking for.
I've got the following XML file:
<?xml version="1.0" standalone="no" ?>
<WndPos name="FrontEnd.Login" l="703" r="1264" t="323" b="909" />
<LayerManager />
<ViewLayers name="RoofLayout" roof="1" nlwalls="1">
<Layer level="-1" module="1" name="Walls" locked="0" visible="1" />
</ViewLayers>
<DirProfiles>
<ProfileInfo ProfileName="ControlCQFT" JobPath="C:\Jobs" DatabasePath="D:\Database\ControlCQFT" />
</DirProfiles>
<DirHistory>
<ProfileInfo Use="Job" Path="C:\Jobs" />
</DirHistory>
I need to replace the entire <DirProfiles> node. The incoming node can be empty, which looks like <DirProfiles />. The new node that I want to insert is in the format:
<DirProfiles>
<ProfileInfo ProfileName="Control1" JobPath="D:\Client1\JobsA" DatabasePath="D:\Database\Control1" />
. . . . .
</DirProfiles>
I tried to handle the problem as a simple string replacement, but I didn't get the result that I needed.
Edit:
My apology, I didn't realized that my xml file is fragment xml document and not full XML as per spec.
Given your input file is a malformed XML file that doesn't contain a root node, you can get around this with a bit of string manipulation.
Here's the file you say you have:
<?xml version="1.0" standalone="no" ?>
<WndPos name="FrontEnd.Login" l="703" r="1264" t="323" b="909" />
<LayerManager />
<ViewLayers name="RoofLayout" roof="1" nlwalls="1">
<Layer level="-1" module="1" name="Walls" locked="0" visible="1" />
</ViewLayers>
<DirProfiles>
<ProfileInfo ProfileName="ControlCQFT" JobPath="C:\Jobs" DatabasePath="D:\Database\ControlCQFT" />
</DirProfiles>
<DirHistory>
<ProfileInfo Use="Job" Path="C:\Jobs" />
</DirHistory>
Here's how to work with it:
var sourceFileName = #"C:\{path}\xml_fragments.txt";
var text = $"<root>{String.Join(Environment.NewLine, File.ReadLines(sourceFileName).Skip(1))}</root>";
var doc = XDocument.Parse(text);
doc.Root.Element("DirProfiles").Elements().Remove();
doc.Root.Element("DirProfiles").Add(
new XElement(
"ProfileInfo",
new XAttribute("ProfileName", "Control1"),
new XAttribute("JobPath", #"D:\Client1\JobsA"),
new XAttribute("DatabasePath", #"D:\Database\Control1")));
That gives me:
<root>
<WndPos name="FrontEnd.Login" l="703" r="1264" t="323" b="909" />
<LayerManager />
<ViewLayers name="RoofLayout" roof="1" nlwalls="1">
<Layer level="-1" module="1" name="Walls" locked="0" visible="1" />
</ViewLayers>
<DirProfiles>
<ProfileInfo ProfileName="Control1" JobPath="D:\Client1\JobsA" DatabasePath="D:\Database\Control1" />
</DirProfiles>
<DirHistory>
<ProfileInfo Use="Job" Path="C:\Jobs" />
</DirHistory>
</root>
See following :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
string xml = File.ReadAllText(FILENAME);
XElement doc = new XElement("Root");
doc.Add(XElement.Parse(xml));
XElement dirProfiles = doc.Descendants("DirProfiles").FirstOrDefault();
XElement profileInfo = dirProfiles.Element("ProfileInfo");
profileInfo.SetAttributeValue("ProfileName", "Control1");
profileInfo.SetAttributeValue("JobPath", #"D:\Client1\JobsA");
profileInfo.SetAttributeValue("DatabasePath", #"D:\Database\Control1");
}
}
}
You can parse the XML data to a C# class model, and after that could do the data changes as per requirement. After changes, you can again parse the model object to XML and rewrite the XML string on the same file.
Refer this link for generate a class structure by available XML: https://www.c-sharpcorner.com/blogs/convert-xml-json-file-to-c-sharp-class
And Use this code to Convert Xml to a C# class object
public static T ConvertXmlToObject<T>(String xml)
{
T result = default(T);
try
{
using (TextReader reader = new StringReader(xml))
{
try
{
result =
(T)new XmlSerializer(typeof(T)).Deserialize(reader);
}
catch (InvalidOperationException)
{
// Throw message for invalid XML
}
}
}
catch (Exception ex)
{
}
return result;
}
Call the function like :
var entity = ConvertXmlToObject<ModelClass>(XMLString);
User this code to convert again Object to XML
public static string ConvertObjectToXML<T>(T ModelClass)
{
XmlSerializer xsObject = new XmlSerializer(typeof(T));
var inputObject = ModelClass;
var xmlString = "";
using (var sw = new StringWriter())
{
using (XmlWriter writer = XmlWriter.Create(sw))
{
xsObject.Serialize(writer, inputObject);
xmlString = sw.ToString();
}
}
return xmlString;
}
Call this function like :
string xmlString = ConvertObjectToXML<ModelClass>(ModelClassObject);
I want to create 1095 e-file xml using c# code. For that i want to create dynamic tags that means if data is available then only creating tags otherwise it can not show that tag.
Here is sample of using xml linq to create your xml file. You can add IF statements as necessary to skip adding any elements
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string header = "<?xml version=\"1.0\" encoding=\"utf-8\" ?><root xmlns:irs=\"myUrl\"></root>";
XDocument doc = XDocument.Parse(header);
XElement root = doc.Root;
XNamespace irs = root.GetNamespaceOfPrefix("irs");
XElement businessName = new XElement("BusinessNameLine1Txt", "Comp Name");
root.Add(businessName);
XElement tinRequest = new XElement(irs + "TINRequestTypeCd");
root.Add(tinRequest);
XElement employerEIN = new XElement(irs + "EmployerEIN", "899090900");
root.Add(employerEIN);
XElement contactNameGrp = new XElement("ContactNameGrp");
root.Add(contactNameGrp);
XElement firstName = new XElement("PersonFirstNm", "First Name Person");
contactNameGrp.Add(firstName);
XElement lastName = new XElement("PersonLastNm", "Last Name Person");
contactNameGrp.Add(lastName);
}
}
}
//sample output
//<?xml version="1.0" encoding="utf-8" ?>
//<root xmlns:irs="myUrl">
//<BusinessName>
// <BusinessNameLine1Txt>Comp Name</BusinessNameLine1Txt>
//</BusinessName>
//<irs:TINRequestTypeCd>BUSINESS_TIN</irs:TINRequestTypeCd>
//<irs:EmployerEIN>899090900</irs:EmployerEIN>
//<ContactNameGrp>
// <PersonFirstNm>First Name Person</PersonFirstNm>
// <PersonLastNm>Last Name Person</PersonLastNm>
//</ContactNameGrp> <ContactPhoneNum>9090909000</ContactPhoneNum>
//</root>
I have an XML Document that stores all sorts of information about users of a system. Utlimately, the nodes that I am intersted in I hae outined below.
So, there is a user that has many user contents - I have included just books as an example.
<?xml version="1.0" encoding="UTF-8"?>
<user>
<userProperties>
<alias val="userAliasOne"/>
<id val="3423423"/>
</userProperties>
<userContent>
<userBooks>
<genre>
<book>
<title>Dummy Value</title>
</book>
</genre>
</userBooks>
</userContent>
</user>
I need to somehow restructure the XML, using XmlDocument and XmlNode so that it matches the below. (userBooks to become root node but all contents of userBooks - /genre/book/title - to stay inside userContent).
<?xml version="1.0" encoding="UTF-8"?>
<userBooks>
<user>
<userProperties>
<alias val="userAliasOne"/>
<id val="3423423"/>
</userProperties>
<userContent>
<genre>
<book>
<title>Dummy Value</title>
</book>
</genre>
</userContent>
</user>
</userBooks>
I've tried selecting the single nodes and cloning them, then appending the clone to the parent and removing the child that's no longer required. It became very long and convoluted and I couldn't get it to work. There must be a more elegant solution that I am not aware of.
Any help appreciated.
Thanks.
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);
List<XElement> userBooks = doc.Descendants("userBooks").ToList();
foreach(XElement userBook in userBooks)
{
userBook.ReplaceWith(userBook.Element("genre"));
}
}
}
}
Here's an example for adding a new root element:
using System;
using System.Xml;
class Test
{
static void Main()
{
XmlDocument doc = new XmlDocument();
doc.Load("test.xml");
var originalRoot = doc.DocumentElement;
doc.RemoveChild(originalRoot);
var newRoot = doc.CreateElement("userBooks");
doc.AppendChild(newRoot);
newRoot.AppendChild(originalRoot);
doc.Save(Console.Out);
}
}
Try using the same approach for "removing" the userBooks element in the original - remove the element from its parent, but then add all the child nodes (of userBooks) as new child nodes of the original parent of userBooks.
In my case I want to add multiple root nodes to the given xml schema. So I have to append the different user elements with it's child nodes in multiple times by appending with the previous contents in the xml file. My problem is how can I add multiple root elements? (I could add one root element but no idea how to add next ).
This is the xml schema
<?xml version="1.0" encoding="utf-8"?>
<SessionId>
<Scource>
<User username="AB">
<DOB>25/5/1980</DOB>
<FirstName>AVS</FirstName>
<LastName>WDW</LastName>
<Location>FWAWE</Location>
</User>
<User username="AqB">
<DOB>25/5/1980</DOB>
<FirstName>AVS</FirstName>
<LastName>WDW</LastName>
<Location>FWAWE</Location>
</User>
</Scource>
</SessionId>
This is my C# code
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
namespace FilteringNightwing
{
class Test
{
static void Main(string[] args) {
string fileLocation = "clients.xml";
if (!File.Exists(fileLocation))
{
XmlTextWriter writer = new XmlTextWriter(fileLocation, null);
writer.WriteStartElement("SessionId");
writer.WriteEndElement();
writer.Close();
}
// Load existing clients and add new
XElement xml = XElement.Load(fileLocation);
xml.Add(new XElement("User",
new XAttribute("username", "AqB"),
new XElement("DOB", "25/5/1980"),
new XElement("FirstName", "AVS"),
new XElement("LastName", "WDW"),
new XElement("Location", "FWAWE")));
xml.Save(fileLocation);
}
}
}
How can I add "Source" tag as another root element. All your answers are welcome.
You can't have more than one root node. Parsing the following will give you an error.
XElement root = XElement.Parse("<a></a><b></b>");
This will error too:
XElement root = XElement.Parse("<a></a><a></a>");
If you want multiple children nodes to the root node, that is perfectly fine. You can have nodes with the same name or different. Such as:
<a>
<b />
<c>
<d />
</c>
<b />
</a>
Add new user to Scource:
XElement xml = XElement.Load(fileLocation);
XElement scource = xml.Element("Scource"); // Find Scource here
scource.Add(new XElement("User", // Add new user to Scource here
new XAttribute("username", "AqB"),
new XElement("DOB", "25/5/1980"),
new XElement("FirstName", "AVS"),
new XElement("LastName", "WDW"),
new XElement("Location", "FWAWE")));
xml.Save(fileLocation);
Try this
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?><SessionId><Source></Source></SessionId>";
XDocument doc = XDocument.Parse(xml);
XElement source = doc.Descendants("Source").FirstOrDefault();
source.Add(new XElement("User", new object[] {
new XAttribute("username", "AqB"),
new XElement("DOB", "25/5/1980"),
new XElement("FirstName", "AVS"),
new XElement("LastName", "WDW"),
new XElement("Location", "FWAWE")
}));
}
}
}
I need to creat an XML in this form:
<?xml version="1.0" encoding="UTF-8"?>
<CastleConfigTop xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="xsd/c5c.xsd" Format="1">
<ProjectInfo ProjectCode="1" ProjectIdentifier="C5_Valeo_SL">
<ProjectName>TheProjectName</ProjectName>
</ProjectInfo>
<Options />
<ConfigFile Name="C5_Valeo_SL">
<History>
</History>
<Includes>
</Includes>
<Options>
</Options>
<DataTypes>
<Options>
<options />
</Options>
<ArgTypes />
<Enums />
<Lookups />
<Doc />
</DataTypes>
<Interfaces />
<Modules>
</Modules>
<ExexutionUnits />
<Doc></Doc>
</ConfigFile>
</CastleConfigTop>
And im using this code, where the object ccTop contains all the necessary classes:
public void saveXmlPath()
{
//xmldoc.Save(this.OutputPath+"\\"+projectName+".C5C");
XmlSerializer serializer = new XmlSerializer(typeof(CastleConfigTop));
string outpath = this.OutputPath + "\\" + projectName + ".C5C";
using (TextWriter writer = new StreamWriter(#outpath))
{
serializer.Serialize(writer, ccTop);
}
}
But the output XMl has the encoding style "utf-8" (lower case) and a different namespace like this:
<?xml version="1.0" encoding="utf-8"?>
<CastleConfigTop xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ProjectInfo ProjectCode="1" ProjectIdentifier="C5_Valeo_SL">
<ProjectName> ProjectName </ProjectName>
</ProjectInfo>
<Options />
<ConfigFile Name="C5_Valeo_SL">
<History>
</History>
<Includes>
</Includes>
<Options>
</Options>
<DataTypes>
<Options />
<ArgTypes />
<Enums />
<Lookups />
<Doc />
</DataTypes>
<Interfaces />
<Modules>
</Modules>
<Difs>
</Difs>
<ExexutionUnits />
<Doc>SAS Volvo SPA</Doc>
</ConfigFile>
</CastleConfigTop>
Any Idea about how to make them exactly similar?
You can post-process this using LINQ to XML to get what you want.
Create an XDocument and serialize your object to this:
var doc = new XDocument();
using (var writer = doc.CreateWriter())
{
serializer.Serialize(writer, ccTop);
}
Then modify the declaration and attributes:
XNamespace xsi = "http://www.w3.org/2001/XMLSchema-instance";
doc.Root.Attributes()
.Where(a => a.IsNamespaceDeclaration && a.Value == "http://www.w3.org/2001/XMLSchema")
.Remove();
doc.Root.Add(new XAttribute(xsi + "noNamespaceSchemaLocation", "xsd/c5c.xsd"));
doc.Root.Add(new XAttribute("Format", 1));
And then write to disc. Note you can use XmlWriterSettings to set various options for writing, such as indentation.
var settings = new XmlWriterSettings
{
Indent = true
};
using (var writer = XmlWriter.Create(FILENAME, settings))
{
doc.WriteTo(writer);
}
Or if you can just use Save (which gives you less options, but the defaults should be fine):
doc.Save(FILENAME);
I wouldn't expect the declaration to be an issue in lower case, but if it is you'll have to modify this using some other means - XmlWriter takes an Encoding and will convert this internally to the lowercase form, so changing this in the XDocument won't help.
TextWriter writer = new StreamWriter(outpath , true, Encoding.ASCII);
You can have a try with these code
Try this
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
saveXmlPath();
}
const string FILENAME = #"c:\temp\test.xml";
public static void saveXmlPath()
{
CastleConfigTop ccTop = new CastleConfigTop();
XmlSerializer serializer = new XmlSerializer(typeof(CastleConfigTop));
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("xsi","xsd/c5c.xsd");
ns.Add("", "http://www.w3.org/2001/XMLSchema-instance");
using (TextWriter writer = new StreamWriter(FILENAME))
{
serializer.Serialize(writer, ccTop, ns);
}
}
}
[XmlRoot(ElementName = "CastleConfigTop", Namespace = "xsi")]
public class CastleConfigTop
{
}
}