Custom Xmlwriter to Skip a Certain Element

Custom xmlWriter to skip a certain element?

Found this in some sample code I wrote previously. It maintains a push-down stack to determine whether to write the element end, which is what you would need to do:

public class ElementSkippingXmlTextWriter : XmlWriterProxy
{
readonly Stack<bool> stack = new Stack<bool>();
readonly Func<string, string, int, bool> filter;
readonly Func<string, string, int, string> nameEditor;
readonly bool filterChildren;

public ElementSkippingXmlTextWriter(XmlWriter writer, Func<string, string, int, bool> filter, bool filterChildren)
: this(writer, filter, null, filterChildren)
{
}

public ElementSkippingXmlTextWriter(XmlWriter writer, Func<string, string, int, bool> filter, Func<string, string, int, string> nameEditor, bool filterChildren)
: base(writer)
{
this.filter = filter ?? delegate { return true; };
this.nameEditor = nameEditor ?? delegate(string localName, string ns, int depth) { return localName; };
this.filterChildren = filterChildren;
}

protected override bool IsSuspended
{
get
{
if (filterChildren)
{
if (!stack.All(b => b))
return true;
}
else
{
if (stack.Count > 0 && !stack.Peek())
return true;
}

return base.IsSuspended;
}
}

public override void WriteStartElement(string prefix, string localName, string ns)
{
var write = filter(localName, ns, stack.Count);
var newLocalName = nameEditor(localName, ns, stack.Count);
if (write)
base.WriteStartElement(prefix, newLocalName, ns);
stack.Push(write);
}

public override void WriteEndElement()
{
if (stack.Pop())
base.WriteEndElement();
}
}

public class XmlWriterProxy : XmlWriter
{
readonly XmlWriter baseWriter;

public XmlWriterProxy(XmlWriter baseWriter)
{
if (baseWriter == null)
throw new ArgumentNullException();
this.baseWriter = baseWriter;
}

protected virtual bool IsSuspended { get { return false; } }

public override WriteState WriteState { get { return baseWriter.WriteState; } }
public override XmlWriterSettings Settings { get { return baseWriter.Settings; } }
public override XmlSpace XmlSpace { get { return baseWriter.XmlSpace; } }
public override string XmlLang { get { return baseWriter.XmlLang; } }

public override void Close()
{
baseWriter.Close();
}

public override void Flush()
{
baseWriter.Flush();
}

public override string LookupPrefix(string ns)
{
return baseWriter.LookupPrefix(ns);
}

public override void WriteBase64(byte[] buffer, int index, int count)
{
if (IsSuspended)
return;
baseWriter.WriteBase64(buffer, index, count);
}

public override void WriteCData(string text)
{
if (IsSuspended)
return;
baseWriter.WriteCData(text);
}

public override void WriteCharEntity(char ch)
{
if (IsSuspended)
return;
baseWriter.WriteCharEntity(ch);
}

public override void WriteChars(char[] buffer, int index, int count)
{
if (IsSuspended)
return;
baseWriter.WriteChars(buffer, index, count);
}

public override void WriteComment(string text)
{
if (IsSuspended)
return;
baseWriter.WriteComment(text);
}

public override void WriteDocType(string name, string pubid, string sysid, string subset)
{
if (IsSuspended)
return;
baseWriter.WriteDocType(name, pubid, sysid, subset);
}

public override void WriteEndAttribute()
{
if (IsSuspended)
return;
baseWriter.WriteEndAttribute();
}

public override void WriteEndDocument()
{
if (IsSuspended)
return;
baseWriter.WriteEndDocument();
}

public override void WriteEndElement()
{
if (IsSuspended)
return;
baseWriter.WriteEndElement();
}

public override void WriteEntityRef(string name)
{
if (IsSuspended)
return;
baseWriter.WriteEntityRef(name);
}

public override void WriteFullEndElement()
{
if (IsSuspended)
return;
baseWriter.WriteFullEndElement();
}

public override void WriteProcessingInstruction(string name, string text)
{
if (IsSuspended)
return;
baseWriter.WriteProcessingInstruction(name, text);
}

public override void WriteRaw(string data)
{
if (IsSuspended)
return;
baseWriter.WriteRaw(data);
}

public override void WriteRaw(char[] buffer, int index, int count)
{
if (IsSuspended)
return;
baseWriter.WriteRaw(buffer, index, count);
}

public override void WriteStartAttribute(string prefix, string localName, string ns)
{
if (IsSuspended)
return;
baseWriter.WriteStartAttribute(prefix, localName, ns);
}

public override void WriteStartDocument(bool standalone)
{
baseWriter.WriteStartDocument(standalone);
}

public override void WriteStartDocument()
{
baseWriter.WriteStartDocument();
}

public override void WriteStartElement(string prefix, string localName, string ns)
{
if (IsSuspended)
return;
baseWriter.WriteStartElement(prefix, localName, ns);
}

public override void WriteString(string text)
{
if (IsSuspended)
return;
baseWriter.WriteString(text);
}

public override void WriteSurrogateCharEntity(char lowChar, char highChar)
{
if (IsSuspended)
return;
baseWriter.WriteSurrogateCharEntity(lowChar, highChar);
}

public override void WriteWhitespace(string ws)
{
if (IsSuspended)
return;
baseWriter.WriteWhitespace(ws);
}
}

It wraps an XmlWriter you would create as usual with a call to XmlWriter.Create(), and filters elements (and, optionally, children) for which the filter delegate returns false.

Note -- not fully tested. Async methods may need to be overridden as well.

Omitting elements using custom XmlTextWriter

No. However you can postpone writing of the element and attributes (put them in a field) until you get to the next element/comment/etc. Then decide whether you want to write them or not.

re-writing large XML Files - excluding certain node

In case of a big file and memory constraints, you should parse with SAX instead of DOM: the XMLReader is the C# equivalent indeed.

This could be a basic approach with a XMLReader for the input, a XMLWriter for the output and a counter to remove nodes named RemoveMe (with all their content).

Notice the internal loop to clone the attributes per each relevant element.

        using (XmlReader reader = XmlReader.Create(OriginalXml))
{
XmlWriterSettings ws = new XmlWriterSettings();
ws.Indent = true;
using (XmlWriter writer = XmlWriter.Create(FilteredXml, ws))
{
int skip = 0;
while (reader.Read())
{
switch (reader.NodeType)
{
case XmlNodeType.Element:
skip += reader.Name.Equals(RemoveMe) ? 1 : 0;
if (skip == 0)
{
writer.WriteStartElement(reader.Name);
while (reader.MoveToNextAttribute())
writer.WriteAttributeString(reader.Name, reader.Value);
}

break;
case XmlNodeType.Text:
if (skip == 0)
{
writer.WriteString(reader.Value);
}
break;
case XmlNodeType.XmlDeclaration:
case XmlNodeType.ProcessingInstruction:
if (skip == 0)
{
writer.WriteProcessingInstruction(reader.Name, reader.Value);
}
break;
case XmlNodeType.Comment:
if (skip == 0)
{
writer.WriteComment(reader.Value);
}
break;
case XmlNodeType.EndElement:
if (skip == 0)
{
writer.WriteFullEndElement();
}
skip -= reader.Name.Equals(RemoveMe) ? 1 : 0;
if (skip < 0)
{
throw new Exception("wrong sequence");
}
break;
}
}

}
}

PHP XMLWriter skip empty tags

You can create condition for that:

if (!empty($fieldVal)) {
$xmlF->writeElement($fieldName, $fieldVal);
}

how to create an xml using xml writer without declaration element

Use XmlWriterSettings.OmitXmlDeclaration.

Don't forget to set XmlWriterSettings.ConformanceLevel to ConformanceLevel.Fragment.

Skipping element when deserializing an XML to list of objects

You can use an XmlTextReader to loop through the individual VehicleMake elements and deserialize them individually like this:

XmlSerializer serializer = new XmlSerializer(typeof(VehicleMake));

List<VehicleMake> result = new List<VehicleMake>();

XmlTextReader reader = new XmlTextReader(new MemoryStream(Encoding.UTF8.GetBytes(xml)));

if(reader.ReadToDescendant("VehicleMake"))
{
do
{
result.Add((VehicleMake)serializer.Deserialize(reader));
} while (reader.ReadToNextSibling("VehicleMake"));
}

Please note that had your XML looked like this (ArrayOfVehicleMake instead of VehicleMakes):

<OUTPUT_XML>
<ArrayOfVehicleMake>
<VehicleMake VehMakeId="1" MakeName="VOLKSWAGEN" VehTypeId="1"/>
<VehicleMake VehMakeId="2" MakeName="OPEL" VehTypeId="1"/>
<VehicleMake VehMakeId="3" MakeName="FORD" VehTypeId="1"/>
<VehicleMake VehMakeId="4" MakeName="VAZ" VehTypeId="1"/>
<VehicleMake VehMakeId="5" MakeName="FIAT" VehTypeId="1"/>
</ArrayOfVehicleMake>
</OUTPUT_XML>

you could have deserialized the entire list without looping through individual elements like this:

XmlSerializer serializer = new XmlSerializer(typeof(List<VehicleMake>));

XmlTextReader reader = new XmlTextReader(new MemoryStream(Encoding.UTF8.GetBytes(xml)));

reader.ReadToDescendant("ArrayOfVehicleMake");

var result = (List<VehicleMake>) serializer.Deserialize(reader);

Create XML with DataTable.WriteXML but with additional Elements?

Try 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
{
static void Main(string[] args)
{
List<Item> items = new List<Item>() {
new Item() { itemNumber = "PHAN-PHN-001", itemDescription = "Standard Phone Package", itemClass = "RETAIL"},
new Item() { itemNumber = "OM0325", itemDescription = "Dual Basic Headset", itemClass = "CATALOG"},
new Item() { itemNumber = "OM01373", itemDescription = "Light Cordless 1", itemClass = "CATALOG"}
};

string header = "<?xml version=\"1.0\" encoding= \"utf-8\" ?>" +
"<GreatPlainIntegration></GreatPlainIntegration>";

XDocument doc = XDocument.Parse(header);
XElement greatPlainsIntegration = (XElement)doc.FirstNode;

greatPlainsIntegration.Add(new object[] {
new XElement("TransmissionDate", DateTime.Now.ToString("yyyy-M-d")),
new XElement("Batch", new object[] {
new XElement("BatchSource", "Inv Mstr"),
new XElement("DocumentElement")
})
});
XElement documentElement = greatPlainsIntegration.Descendants("DocumentElement").FirstOrDefault();
documentElement.Add(items.Select(x => new XElement("Item", new XElement[] {
new XElement("ItemNumber", x.itemNumber),
new XElement("ItemDescription", x.itemDescription),
new XElement("ItemClass", x.itemClass)
})).ToArray());

}
}
public class Item
{
public string itemNumber { get; set; }
public string itemDescription { get; set; }
public string itemClass { get; set; }
}
}

XmlWriter async methods

First off, I do have to question the benchmarking. 3-4 times slower on 100MB files is really significant.

But regardless, async is not about doing things faster. It's about doing something else while that operation is going on. On the client side, you get the benefit of responsiveness; on the server side, you get the benefit of scalability.

The tradeoff is that the operation itself is actually slower (but it should be just a little slower, not 3-4 times slower). It's likely that you're not using a truly asynchronous stream for writing (you have to specifically open a file stream asynchronously to get an asynchronous stream).



Related Topics



Leave a reply



Submit