Posso usar namespaces predefinidos ao carregar um XDocument?
-
22-08-2019 - |
Pergunta
Muitas vezes eu tenho que lidar com documentos XML que contêm elementos de namespace, mas não declara o namespace. Por exemplo:
<root>
<a:element/>
</root>
Uma vez que o prefixo "a" nunca é atribuído um espaço de nomes URI, o documento é inválido. Quando eu carregar tal um documento XML usando o seguinte código:
using (StreamReader reader = new StreamReader(new FileStream(inputFileName,
FileMode.Open, FileAccess.Read, FileShare.ReadWrite))) {
doc = XDocument.Load(reader, LoadOptions.PreserveWhitespace);
}
ele lança uma exceção dizendo (com razão) que o documento contém um namespace não declarado e não é bem formado.
Então, eu posso predefinir prefixo namespace padrão -> pares namespace URI para o analisador para voltar a cair? XmlNamespaceManager parece promissor, mas não sabe como aplicá-la a esta situação (ou se eu puder).
Solução
Você pode criar um XmlReader
com um XmlParserContext
que sabe sobre os namespaces; os seguintes trabalhos para XmlDocument
e XDocument
:
class SimpleNameTable : XmlNameTable {
List<string> cache = new List<string>();
public override string Add(string array) {
string found = cache.Find(s => s == array);
if (found != null) return found;
cache.Add(array);
return array;
}
public override string Add(char[] array, int offset, int length) {
return Add(new string(array, offset, length));
}
public override string Get(string array) {
return cache.Find(s => s == array);
}
public override string Get(char[] array, int offset, int length) {
return Get(new string(array, offset, length));
}
}
static void Main() {
XmlNamespaceManager mgr = new XmlNamespaceManager(new SimpleNameTable());
mgr.AddNamespace("a", "http://foo/bar");
XmlParserContext ctx = new XmlParserContext(null, mgr, null,
XmlSpace.Default);
using (XmlReader reader = XmlReader.Create(
new StringReader(@"<root><a:element/></root>"), null, ctx)) {
XDocument doc = XDocument.Load(reader);
//XmlDocument doc = new XmlDocument();
//doc.Load(reader);
}
}
Outras dicas
Com base na resposta anterior, você pode preservar os prefixos de namespace por primeiro carregamento em um XmlDocument e analisar o OuterXml do XmlDocument em um XDocument
XDocument LoadWithPrefix(Stream stream)
{
XmlNamespaceManager mgr = new XmlNamespaceManager(new NameTable());
mgr.AddNamespace("a", "http://foo/bar");
XmlParserContext ctx = new XmlParserContext(null, mgr, null, XmlSpace.Default);
using (XmlReader reader = XmlReader.Create(stream, null, ctx))
{
XmlDocument doc = new XmlDocument();
doc.Load(reader);
return XDocument.Parse(doc.OuterXml);
}
}