Fastest Way to Add New Node to End of an Xml

Fastest way to add new node to end of an xml?

You need to use the XML inclusion technique.

Your error.xml (doesn't change, just a stub. Used by XML parsers to read):

<?xml version="1.0"?>
<!DOCTYPE logfile [
<!ENTITY logrows
SYSTEM "errorrows.txt">
]>
<Errors>
&logrows;
</Errors>

Your errorrows.txt file (changes, the xml parser doesn't understand it):

<Error>....</Error>
<Error>....</Error>
<Error>....</Error>

Then, to add an entry to errorrows.txt:

using (StreamWriter sw = File.AppendText("logerrors.txt"))
{
XmlTextWriter xtw = new XmlTextWriter(sw);

xtw.WriteStartElement("Error");
// ... write error messge here
xtw.Close();
}

Or you can even use .NET 3.5 XElement, and append the text to the StreamWriter:

using (StreamWriter sw = File.AppendText("logerrors.txt"))
{
XElement element = new XElement("Error");
// ... write error messge here
sw.WriteLine(element.ToString());
}

See also Microsoft's article Efficient Techniques for Modifying Large XML Files

Append a New Node in already existing XML Document

Few things you should be aware of:

  1. The Xml closing tag is missing /.

  2. Xml is case sensitive. <HBeds> and <Hbeds> are different!

Here's an example how we can insert <HId> nodes as asked in the question.

In my Project I have the xml file, "MyXml.xml" :

<?xml version="1.0" encoding="utf-8" ?>
<NewDataSet>
<Table>
<HotelName>A</HotelName>
<Rating>5*</Rating>
<Hzone>Central </Hzone>
<HBeds>B</HBeds>
<Address>Lodhi Road</Address>
<Soh>-</Soh>
<Recommended>0</Recommended>
<DetailStr>-</DetailStr>
<Block>N</Block>
</Table>
<Table>
<HotelName>B</HotelName>
<Rating>5*</Rating>
<Hzone>Central </Hzone>
<HBeds>A</HBeds>
<Address>Lodhi Road</Address>
<Soh>Bh</Soh>
<Recommended>0</Recommended>
<DetailStr>-</DetailStr>
<Block>N</Block>
</Table>
</NewDataSet>

I have a test webform WebForm1.aspx with this markup:

<%@ Page Language="vb" AutoEventWireup="false" CodeBehind="WebForm1.aspx.vb" Inherits="VBXmlTest.WebForm1" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:TextBox ID="txtxmlfile" runat="server">Vancouver</asp:TextBox>
</div>
</form>
</body>
</html>

In the code behind, in Page_Load I am calling ProcessXml function, My WebForm1.aspx.vb is:

Imports System.Xml

Public Class WebForm1
Inherits System.Web.UI.Page

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
ProcessXml()
End Sub

Private Sub ProcessXml()
Dim xDoc As New XmlDocument
Dim filePath As String = Server.MapPath("~/MyXml.xml")
xDoc.Load(filePath)
Dim XmlNodeListPackes As XmlNodeList
XmlNodeListPackes = xDoc.SelectNodes("/NewDataSet/Table")
Dim Hid As Integer
For Each xNode As XmlNode In XmlNodeListPackes

Dim strHotelBeds As String = xNode.SelectSingleNode("HBeds").InnerText
Dim City As String = txtxmlfile.Text
Hid = DAL.GetHotelBedId(strHotelBeds, City)
Dim hidNode As XmlNode = xNode.SelectSingleNode("HId")
If hidNode IsNot Nothing Then
hidNode.InnerText = Hid
Else
hidNode = xDoc.CreateNode(XmlNodeType.Element, "HId", "")
hidNode.InnerText = Hid
xNode.AppendChild(hidNode)
End If
Next
xDoc.Save(filePath)
End Sub

End Class

And to test, I have replaced the DAL with a Module, just for testing:

Public Module DAL
Public Function GetHotelBedId(ByVal strHotelBeds As String, ByVal City As String) As Integer
Return 1
End Function
End Module

And this is the output xml, no matter how many times I run it:

<?xml version="1.0" encoding="utf-8"?>
<NewDataSet>
<Table>
<HotelName>A</HotelName>
<Rating>5*</Rating>
<Hzone>Central </Hzone>
<HBeds>B</HBeds>
<Address>Lodhi Road</Address>
<Soh>-</Soh>
<Recommended>0</Recommended>
<DetailStr>-</DetailStr>
<Block>N</Block>
<HId>1</HId>
</Table>
<Table>
<HotelName>B</HotelName>
<Rating>5*</Rating>
<Hzone>Central </Hzone>
<HBeds>A</HBeds>
<Address>Lodhi Road</Address>
<Soh>Bh</Soh>
<Recommended>0</Recommended>
<DetailStr>-</DetailStr>
<Block>N</Block>
<HId>1</HId>
</Table>
</NewDataSet>

How do I insert a new node to an existing XML

Looks like there's not such API in Android. However, You still have the following options to fix the issue:

  • Look for some open-source library which provides such ability;
  • Do some manual string operations still using XmlSerializer, like provided below:

    private void testXMLFiles() {
    //create a new file called "new.xml" in the SD card
    final File newXmlFile = new File(Environment.getExternalStorageDirectory() + "/download/teste/audit.xml");
    RandomAccessFile randomAccessFile = null;
    final boolean fileExists = newXmlFile.exists();
    String lastLine = null;

    if (fileExists) {
    try {
    randomAccessFile = new RandomAccessFile(newXmlFile, "rw");
    randomAccessFile.seek(0);

    if (null != randomAccessFile) {
    final Scanner scanner = new Scanner(newXmlFile);
    int lastLineOffset = 0;
    int lastLineLength = 0;

    while (scanner.hasNextLine()) {
    // +1 is for end line symbol
    lastLine = scanner.nextLine();
    lastLineLength = lastLine.length() + 2;
    lastLineOffset += lastLineLength;
    }

    // don't need last </root> line offset
    lastLineOffset -= lastLineLength;

    // got to string before last
    randomAccessFile.seek(lastLineOffset);
    }
    } catch(FileNotFoundException e) {
    Log.e("FileNotFoundException", "can't create FileOutputStream");
    } catch (IOException e) {
    Log.e("IOException", "Failed to find last line");
    }
    } else {
    try {
    newXmlFile.createNewFile();
    } catch(IOException e) {
    Log.e("IOException", "exception in createNewFile() method");
    }

    try {
    randomAccessFile = new RandomAccessFile(newXmlFile, "rw");
    } catch(FileNotFoundException e) {
    Log.e("FileNotFoundException", "can't create FileOutputStream");
    }
    }

    //we create a XmlSerializer in order to write xml data
    XmlSerializer serializer = Xml.newSerializer();

    if (randomAccessFile == null) {
    return;
    }

    try {
    final StringWriter writer = new StringWriter();

    serializer.setOutput(writer);

    if (!fileExists) {
    serializer.startDocument(null, true);
    serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
    serializer.startTag(null, "root");
    } else {
    serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
    }

    serializer.startTag(null, "child1");
    serializer.endTag(null, "child1");

    serializer.startTag(null, "child2");
    serializer.attribute(null, "attribute", "value");
    serializer.endTag(null, "child2");

    serializer.startTag(null, "child3");
    serializer.text("some text inside child3");
    serializer.endTag(null, "child3");

    if (!fileExists) {
    serializer.endTag(null, "root");
    }

    serializer.flush();

    if (lastLine != null) {
    serializer.endDocument();
    writer.append(lastLine);
    }

    // Add \n just for better output in console
    randomAccessFile.writeBytes(writer.toString() + "\n");
    randomAccessFile.close();

    Toast.makeText(getApplicationContext(), "Save!", Toast.LENGTH_SHORT).show();
    } catch (Exception e) {
    Log.e("Exception","error occurred while creating xml file");
    e.printStackTrace();
    }
    }

Its output after second run is the following (quite similar to what You expect):

<?xml version='1.0' standalone='yes' ?>
<root>
<child1 />
<child2 attribute="value" />
<child3>some text inside child3</child3>

<child1 />
<child2 attribute="value" />
<child3>some text inside child3</child3></root>
  • Store all tags from initial xml (e.g. using SAXParser you can read tags, write to new file the same time and apppend new ones at the end using XMLSerializer);

Add new Node to the xml file

You need to ImportNode and AppendChild it then: xDocumentArchived.DocumentElement.AppendChild(xDocumentArchived.ImportNode(node, true);. And at the end you need to call Save on xDocumentArchived: xDocumentArchived.Save(archivedXmlFile);.

How I can insert node at beginning xml file?

Why cant you use the firstChild method and then insert before? Like

n.insertBefore(a, n.getFirstChild());

Full code

public void insertNewProject(Project entity) {
String filePath = "location.xml";
File xmlFile = new File(filePath);
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder;
try {
dBuilder = dbFactory.newDocumentBuilder();
Document doc;

doc = dBuilder.parse(xmlFile);

doc.getDocumentElement().normalize();

Node n = doc.getElementsByTagName("n").item(0);

Element a = doc.createElement("a");
n.insertBefore(a, n.getFirstChild());

Element b = doc.createElement("b");
b.appendChild(doc.createTextNode(entity.getLocation()));
a.appendChild(b);

Element c = doc.createElement("c");
c.appendChild(doc.createTextNode(entity.getName()));
a.appendChild(c);

doc.getDocumentElement().normalize();

TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
DOMSource domSource = new DOMSource(doc);
StreamResult streamResult = new StreamResult(new File("location.xml"));
transformer.transform(domSource, streamResult);

} catch (ParserConfigurationException pce) {
return;
} catch (IOException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (TransformerException tfe) {
return;
}
}

How do I add and rearrange nodes of an XML file in R?

The "xml2" package has a simple syntax for adding and removing nodes from an XML document.

To rearrange the structure you first need to locate and copy the nodes you want to move, then remove them from the document and then add original list back into the document. I would not recommend this unless you have a very specific reason for doing so.

library(xml2)
library(magrittr)

page <- read_xml("<dataset>
<language>
<name>Old_Irish</name>
<definite_source>Demonstrative1</definite_source>
<n_cases>5</n_cases>
</language>
<language>
<name>Irish</name>
<definite_source>Demonstrative2</definite_source>
<n_cases>4</n_cases>
</language>
</dataset>")

#Find the nodes you want to relocate
nodesdefinite <- page %>% xml_find_all(".//definite_source")
html_structure(page)
#Remove the Nodes
page %>% xml_find_all(".//definite_source") %>% xml_remove()
#Add them where desired
page %>% xml_find_all(".//n_cases") %>% xml_add_sibling(nodesdefinite, .where="after")

#Verify the structure
html_structure(page)
page %>% xml_find_all(".//definite_source")

#To add a node
#Using the add_child, add the new node at the end of the list
page %>% xml_find_all(".//language") %>% xml_add_child("newNode", "new value")
html_structure(page) #Verify the structure

#Using the add_sibling, better control for placement
page %>% xml_find_all(".//n_cases") %>% xml_add_sibling("newSibling", pi, .where="before")

Easiest way to add xml a node with a bunch of children nodes in .Net?

If you can use .NET 4, I would recommend looking at XDocument. Here is an example for adding elements to a document:

http://msdn.microsoft.com/en-us/library/system.xml.linq.xdocument.add.aspx

You can use XDocument.Parse to initialize the document from a string, or XDocument.Load to initialize from a file (there are other overloads too).

Then, you can navigate to the element where you want to insert, and do an XElement.Add()

Here is some sample code that puts your XML element into another XDocument. I'm just adding the XML to the first "Elem" node of the target XDcoument, but you can tweak the code to add it multiple times, etc...

        public static void Main()
{

var xelementToAdd = XElement.Parse(@"
<TablixRow>
<Height>0.23622in</Height>
<TablixCells>
<TablixCell>
<CellContents>
<Textbox Name='Textbox1'>
<CanGrow>true</CanGrow>
<KeepTogether>true</KeepTogether>
<Paragraphs>
<Paragraph>
<TextRuns>
<TextRun>
<Value/>
<Style/>
</TextRun>
</TextRuns>
<Style/>
</Paragraph>
</Paragraphs>
<DefaultName>Textbox1</DefaultName>
<Style>
<Border>
<Style>None</Style>
</Border>
<PaddingLeft>2pt</PaddingLeft>
<PaddingRight>2pt</PaddingRight>
<PaddingTop>2pt</PaddingTop>
<PaddingBottom>2pt</PaddingBottom>
</Style>
</Textbox>
</CellContents>
</TablixCell>
<TablixCell>
<CellContents>
<Textbox Name='Textbox5'>
<CanGrow>true</CanGrow>
<KeepTogether>true</KeepTogether>
<Paragraphs>
<Paragraph>
<TextRuns>
<TextRun>
<Value/>
<Style/>
</TextRun>
</TextRuns>
<Style/>
</Paragraph>
</Paragraphs>
<DefaultName>Textbox5</DefaultName>
<Style>
<Border>
<Style>None</Style>
</Border>
<PaddingLeft>2pt</PaddingLeft>
<PaddingRight>2pt</PaddingRight>
<PaddingTop>2pt</PaddingTop>
<PaddingBottom>2pt</PaddingBottom>
</Style>
</Textbox>
</CellContents>
</TablixCell>
</TablixCells>
</TablixRow>");

// you might use XDocument.Load() here instead of Parse()
var xdoc = XDocument.Parse(@"
<Report xmlns='http://schemas.microsoft.com/sqlserver/reporting/2008/01/reportdefinition'
xmlns:rd='http://schemas.microsoft.com/SQLServer/reporting/reportdesigner'
xmlns:msxsl='urn:schemas-microsoft-com:xslt'
xmlns:xs='http://www.w3.org/2001/XMLSchema'
xmlns:msdata='urn:schemas-microsoft-com:xml-msdata'>
<rd:DrawGrid>true</rd:DrawGrid>
<ReportParameters></ReportParameters>
</Report>
");

XNamespace ns1 = "http://schemas.microsoft.com/sqlserver/reporting/2008/01/reportdefinition";
XNamespace ns2 = "http://schemas.microsoft.com/SQLServer/reporting/reportdesigner";
XNamespace ns3 = "urn:schemas-microsoft-com:xslt";
XNamespace ns4 = "http://www.w3.org/2001/XMLSchema";
XNamespace ns5 = "urn:schemas-microsoft-com:xml-msdata";

var e = xdoc.Root.Elements(ns1 + "ReportParameters")
.FirstOrDefault();

e.Add(xelementToAdd);
xdoc.Save(@"c:\temp\foo2.xml");
}

And for what it's worth, I tweaked your sample XML to remove the namespace prefix (namespaces are an issue separate from your question) and replaced the double quotes with single quotes (the XML is otherwise equivalent).

Updated: Yes, you are running into a namespace problem. Even though your <ReportParameters> element does not have a prefix like <rd:DrawGrid>, it's using the default namespace, and you must specify it. Take a look at the updated sample, which should do what you want. Hope this helps!

How to add new Node to xml file

I usually use Linq's XDocument to deal with XML. You would need to add a System.Xml.Linq to your using statements. It would be something like:

        string movieListXML = @"c:\test\movies.xml";
XDocument doc = XDocument.Load(movieListXML);
foreach (XElement movie in doc.Root.Descendants("Movie"))
{
movie.Add(new XElement("Time", "theTime"));
}
doc.Save(movieListXML);


Related Topics



Leave a reply



Submit