all groups > dotnet xml > january 2005 >
You're in the

dotnet xml

group:

Insert Doctype Node Dynamically


Insert Doctype Node Dynamically Chris Fink
1/27/2005 3:37:22 PM
dotnet xml:
I am receiving xml documents from a customer without a reference to a
doctype. I know what the Doctype DTD should be need to insert the
declaration as follows

<?xml version="1.0" encoding="UTF-8"?>
<!-- start of add validation tag -->
<!DOCTYPE D1XML SYSTEM "http://url/myDTD.DTD">
<!-- end of add validation tag -->
<XML.....>

Using one of the provided framework classes such as XmlValidatingReader,
etc.... is there a method to dynamically add a doctype reference? Or do I
have to find the starting node and perform an insert to the doctype
afterwards? Bottom line is I need a foolproof way to add this doctype
reference in the right spot each time.

For all of you asking, "Why...." The customer will be adding this DTD
reference shortly, but for now this is the "band-aid" that needs to be
applied.

Thanks for your help.

Re: Insert Doctype Node Dynamically Derek Harmon
1/28/2005 12:25:55 AM
[quoted text, click to view]

While streaming it through an XmlValidatingReader, one possibility
is to subclass one of the Reader classes from the System.IO name-
space to insert the additional line(s) you seek for the reference to
an external DTD.

Here is an example of such a class that could do this (I only impl'd
the Read(char[],int,int) method because in a quick test, that's what
was used when loading an XmlDocument as you may if you wrap
this Reader in an XmlTextReader -- so your mileage may vary.)

- - - InsertLineStreamReader.cs (partial)
using System;
using System.Collections;
using System.IO;

namespace Derek.IO
{
public class InsertLineStreamReader : System.IO.StreamReader
{
private delegate int ReadMethod(char[] buffer, int index, int count);

string insertStr;
int lineNo, readNo, overflowIndex;
char[] overflow;

ReadMethod[] fnRead;

public InsertLineStreamReader(Stream source, string insertStr, int lineNo) : base( source)
{
this.insertStr = insertStr;
this.lineNo = lineNo;
this.readNo = 0;
this.fnRead = new ReadMethod[] {
new ReadMethod( this.ReadBefore),
new ReadMethod( this.ReadOverflow),
new ReadMethod( this.ReadAfter)
};
this.overflowIndex = 0;
}

//
// . . . Override other methods here (may not be necessary depending on the consumer).
//

public override int Read(char[] buffer, int index, int count)
{
// Use dispatch table of delegates to get the complex override(s)
// out of the way asap.
return this.fnRead[this.readNo](buffer, index, count);
}

public int ReadBefore(char[] buffer, int index, int count)
{
char[] scan = new char[count];
int cchRead = base.Read(scan, 0, count);
if (cchRead > -1)
{
int insOfs, lineCnt = 0;
for (insOfs = 0; insOfs < cchRead && this.lineNo != lineCnt; ++insOfs)
{
if (scan[insOfs] == '\n') ++lineCnt;
}
if (this.lineNo == lineCnt)
{
char[] insertBuf = this.insertStr.ToCharArray( );
if ((insOfs + this.insertStr.Length) > count)
{
int fragmentLen = (insOfs + insertBuf.Length - count);
Array.Copy(scan, 0, buffer, index, insOfs);
Array.Copy(insertBuf, 0, buffer, index + insOfs, fragmentLen);
this.overflow = new char[insertBuf.Length - fragmentLen];
Array.Copy(insertBuf, fragmentLen, this.overflow, 0, this.overflow.Length);
this.readNo = 1;
cchRead = count;
}
else
{
Array.Copy(scan, 0, buffer, index, insOfs);
Array.Copy(insertBuf, 0, buffer, index + insOfs, insertBuf.Length);
int destSpaceRemaining = count - insOfs - insertBuf.Length;
int srcSpaceRemaining = cchRead - insOfs;
if (srcSpaceRemaining > destSpaceRemaining)
{
Array.Copy(scan, insOfs, buffer, index + insOfs + insertBuf.Length, destSpaceRemaining);
this.overflow = new char[srcSpaceRemaining - destSpaceRemaining];
Array.Copy(scan, insOfs + destSpaceRemaining, this.overflow, 0, this.overflow.Length);
this.readNo = 1;
cchRead = count;
}
else
{
Array.Copy(scan, insOfs, buffer, index + insOfs + insertBuf.Length, srcSpaceRemaining);
this.readNo = 2;
cchRead = cchRead + insertBuf.Length;
}
}
}
else
{
Array.Copy(scan, 0, buffer, index, cchRead);
}
}
return cchRead;
}

public int ReadOverflow(char[] buffer, int index, int count)
{
int cchRead;
int overflowLen = this.overflow.Length - this.overflowIndex;

if (count > overflowLen)
{
Array.Copy(this.overflow, this.overflowIndex, buffer, index, overflowLen);
this.overflowIndex = this.overflow.Length;
this.readNo = 2;
cchRead = overflowLen;

int nextCchRead = this.Read( buffer, index + overflowLen, count - overflowLen);
return ( -1 == nextCchRead ) ? cchRead : ( cchRead + nextCchRead );
}

Array.Copy(this.overflow, this.overflowIndex, buffer, index, count);
this.overflowIndex += count;
cchRead = count;
return cchRead;
}

public int ReadAfter(char[] buffer, int index, int count)
{
return base.Read(buffer, index, count);
}
}
}
- - -

Now you might use this to insert an arbitrary line into the beginning of
your XML instance document like this,

FileStream stream = new FileStream("doc.xml", FileMode.Open, FileAccess.Read);
string dtd = "<!-- Ref to your DTD can go here. -->";
InsertLineStreamReader reader = new InsertLineStreamReader(stream, dtd, 1);
XmlValidatingReader xmlReader = new XmlValidatingReader(new XmlTextReader( reader));
// ... wire-up Schema and ValidationEventHandler here.
XmlDocument doc = new XmlDocument( );
doc.Load( xmlReader);

As far as the XmlValidatingReader is concerned, it will receive,

<?xml version="1.0" encoding="UTF-8" ?>
<!-- Ref to your DTD can go here. -->
<XML ... />

even though your file contained,

<?xml version="1.0" encoding="UTF-8" ?>
<XML ... />


Derek Harmon

AddThis Social Bookmark Button