Я использую C# с классами System.Xml для управления существующим XML-документом.
В частности, мне нужно вставить фрагменты XML, которые у меня есть в строковой форме, в определенные узлы документа.
Для этого я создаю новый элемент, а затем устанавливаю свою XML-строку в его свойстве InnerXml, например:
using System.Xml;
string nsUri = "http://myorg/myns";
string baseDocument = @$"<?xml version = ""1.0"" encoding = ""utf-16""?><Root xmlns = ""{nsUri}""></Root>";
XmlDocument doc = new XmlDocument();
doc.LoadXml(baseDocument);
var nsm = new XmlNamespaceManager(doc.NameTable);
nsm.AddNamespace("default", nsUri);
var target = doc.CreateElement("Target", nsUri);
target.InnerXml= @"
<Sub1>This is a subnode</Sub1>
<Sub2>Here is another subnode</Sub2>
";
doc.DocumentElement.AppendChild(target);
doc.Save(@"C:\text.xml");
Глядя на вывод, вот результат:
<?xml version = "1.0" encoding = "utf-16"?>
<Root xmlns = "http://myorg/myns">
<Target>
<Sub1 xmlns = "">This is a subnode</Sub1>
<Sub2 xmlns = "">Here is another subnode</Sub2>
</Target>
</Root>
Как видите, есть проблема: каждому подузлу внутри вставленной мной строки XML присваивается пустой атрибут xmlns: xmlns = "".
Это само по себе уже проблема, но гораздо хуже то, что после этого документ оказывается в несогласованном состоянии, и дальнейшие манипуляции приводят к странным результатам. Например, если после этого я делаю:
var sub1 = target.SelectSingleNode("default:Sub1", nsm);
тогда sub1 будет null.
Итак, видимо, это неправильный способ сделать это, и проблема возникает, когда я переназначаю InnerXml. Можете ли вы сказать мне, как правильно это сделать?
Некоторые примечания:
XmlDocumentFragment, но результат точно такой жеЛучше использовать LINQ to XML API. Он доступен в .Net Framework с 2007 года.
@YitzhakKhabinsky: ты прочитал вопрос? Код, который я предоставил, ЯВЛЯЕТСЯ минимальным воспроизводимым примером, он ДЕЙСТВИТЕЛЬНО содержит правильно сформированный входной XML, и вы можете запустить его напрямую, и он выдаст выходные данные, которые я показал. Что касается использования LINQ to XML: можно ли смешивать эти два подхода? Поскольку существующая кодовая база довольно велика и везде используется System.Xml, я, к сожалению, не могу ее полностью переписать, даже если бы захотел.
@ user246821: эти решения, похоже, не применимы к моему вопросу... как я уже сказал: я хочу вставить в документ весь (потенциально очень длинный) фрагмент XML, решения, которые вы связали, создают отдельные элементы и атрибуты один за другим, что в моем случае было бы безумием
К сожалению, неясно, чего вы пытаетесь достичь. Вы упомянули, что мне нужно вставить фрагменты XML, которые у меня есть в строковой форме, в определенные узлы документа. Однако предоставленный вами код этого не демонстрирует. Где находится переменная, содержащая строковые данные? Если переменная содержит жестко запрограммированные значения, которые вы добавили без использования переменной, почему эта информация вообще находится в строке? Откуда это?
Как изменить или унаследовать пространство имен по умолчанию при настройке InnerXml отвечает на ваш вопрос?
Похоже на ответ из Как изменить или унаследовать пространство имен по умолчанию при настройке InnerXml действительно работает: target.SetInnerXmlAndInheritDefaultNamespace(innerXml); дает желаемый результат, см. dotnetfiddle.net/fIw5W6.
Одно замечание по поводу SetInnerXmlAndInheritDefaultNamespace: вам необходимо добавить элемент target к его родительскому элементу перед установкой внутреннего XML. Если вы этого не сделаете, вы можете получить избыточные объявления пространства имен. В любом случае отметить как дубликат?
@dbc: +1, в итоге я использовал более простое решение, предложенное ниже, но я проверил ответ, на который вы ссылаетесь, и он также работает (единственный недостаток, как вы сказали, заключается в том, что он работает только в том случае, если узел, который вы внедряете XML-код уже добавлен к его последнему родительскому узлу, поэтому вы должны помнить, что всегда следует использовать этот порядок)





Я не вижу действительно простого способа смешать текст DOM и XML, подобный этому (ни один API высокого уровня не поддерживает фрагменты XML), но выбрав и используя XmlReader, вы можете это сделать. Также перехожу на XDocument, EG
using System.Xml;
using System.Xml.Linq;
XNamespace ns = "http://myorg/myns";
string baseDocument = @$"<?xml version = ""1.0"" encoding = ""utf-16""?><Root xmlns = ""{ns.ToString()}""></Root>";
XDocument doc = XDocument.Parse(baseDocument);
var target = new XElement(ns + "Target");
var fragment = @"
<Sub1>This is a subnode</Sub1>
<Sub2>Here is another subnode</Sub2>
";
var settings = new XmlReaderSettings() { ConformanceLevel = ConformanceLevel.Fragment };
XmlReader reader = XmlReader.Create(new StringReader(fragment), settings);
while (reader.Read())
{
while (reader.NodeType != XmlNodeType.Element)
{
if (!reader.Read()) break;
}
if (reader.EOF) break;
var sr = reader.ReadSubtree();
XElement subNode = XElement.Load(sr);
subNode.Name = ns + subNode.Name.LocalName;
target.Add(subNode);
}
doc.Root.Add(target);
Console.WriteLine(doc.ToString());
XmlElement.InnerXml очевидно считает элемент без явного пространства имен принадлежащим пустому пространству имен. Поэтому лучшее решение — устранить проблему в источнике: убедитесь, что все, что генерирует вашу XML-строку, указывает правильное пространство имен для каждого элемента. (Иначе вне контекста непонятно, к какому пространству имен принадлежат <Sub1> и <Sub2>.)
Однако, если это невозможно, и вы готовы предположить, что все элементы без атрибута пространства имен в вашей строке XML принадлежат указанному пространству имен по умолчанию, тогда альтернативный подход — создать фрагмент документа, установить его InnerXml в вашу строку XML. в контексте с желаемым пространством имен по умолчанию, а затем добавьте фрагмент в документ:
// As before...
string nsUri = "http://myorg/myns";
string baseDocument = @$"<?xml version = ""1.0"" encoding = ""utf-16""?><Root xmlns = ""{nsUri}""></Root>";
XmlDocument doc = new XmlDocument();
doc.LoadXml(baseDocument);
var nsm = new XmlNamespaceManager(doc.NameTable);
nsm.AddNamespace("default", nsUri);
// Create the <Target> element using CreateDocumentFragment instead of CreateElement:
var fragment = doc.CreateDocumentFragment();
fragment.InnerXml = @$"<Target xmlns = ""{nsUri}"">
<Sub1>This is a subnode</Sub1>
<Sub2>Here is another subnode</Sub2>
</Target>";
var node = doc.DocumentElement.AppendChild(fragment);
node.Attributes.RemoveNamedItem("xmlns"); // Remove the redundant xmlns attribute on <Target>.
doc.Save(@"C:\text.xml");
Вот результат:
<?xml version = "1.0" encoding = "utf-16"?>
<Root xmlns = "http://myorg/myns">
<Target>
<Sub1>This is a subnode</Sub1>
<Sub2>Here is another subnode</Sub2>
</Target>
</Root>
Это единственное решение, которое, похоже, работает правильно для моего варианта использования. Я не думал просто удалять xmlns, как будто это просто еще один случайный атрибут, но это имеет смысл, поскольку в корневом документе уже указано пространство имен по умолчанию. Спасибо!
Вы были почти там. Единственное, что вам нужно — создать узлы subs и добавить их в файл target. Что-то вроде этого.
using System.Xml;
string nsUri = "http://myorg/myns";
string baseDocument = @$"<?xml version = ""1.0"" encoding = ""utf-16""?><Root xmlns = ""{nsUri}""></Root>";
XmlDocument doc = new XmlDocument();
doc.LoadXml(baseDocument);
var nsm = new XmlNamespaceManager(doc.NameTable);
nsm.AddNamespace("default", nsUri);
var target = doc.CreateElement("Target", nsUri);
var sub =doc.CreateElement("Sub1", nsUri);
sub.InnerXml = "This is a subnode";
target.AppendChild(sub);
sub = doc.CreateElement("Sub2", nsUri);
sub.InnerXml = "Here is another subnode";
target.AppendChild(sub);
doc.DocumentElement?.AppendChild(target);
Console.WriteLine(doc.OuterXml);
Код производит именно то, что вы хотите. Вы также можете получить доступ к элементам.
var subBak = target.SelectSingleNode("default:Sub1", nsm);
Вот еще одно решение с использованием LINQ to XML.
С#
void Main()
{
XDocument xdoc = XDocument.Parse(@"<Root xmlns='http://myorg/myns'>
</Root>");
XElement fragment = XElement.Parse(@"<Target>
<Sub1>This is a subnode</Sub1>
<Sub2>Here is another subnode</Sub2>
</Target>");
xdoc.Root.Add(fragment);
foreach (var node in xdoc.Descendants()
.Where(x => x.Name.NamespaceName == ""))
{
// Inherit the parent namespace instead
node.Name = node.Parent.Name.Namespace + node.Name.LocalName;
}
xdoc.Save(@"e:\temp.xml");
}
Выход
<?xml version = "1.0" encoding = "utf-8"?>
<Root xmlns = "http://myorg/myns">
<Target>
<Sub1>This is a subnode</Sub1>
<Sub2>Here is another subnode</Sub2>
</Target>
</Root>
Задавая вопрос, вам необходимо предоставить минимальный воспроизводимый пример: Отредактируйте исходный вопрос и предоставьте следующее: (1) Образец правильно сформированного XML-файла. (2) Что вам нужно сделать, то есть логика, и ваш код пытается ее реализовать. (3) Желаемый результат основан на примере данных в № 1 выше.