Как изменить пространство имен XML определенного элемента
у меня есть некоторый набор xml, созданный с помощью xmlserialization некоторых сообщений WCF.
Теперь я хочу сделать общий метод, в котором я предоставлю имя файла xml и префикс, такой как mailxml12
.
Затем в xml-файле те элементы, которые не имеют префикса пространства имен в их имени, должны быть заменены на mailxml12:
как исходный файл:
<DeliveryApptCreateRequest d2p1:ApptType="Pallet" d2p1:PickupOrDelivery="Delivery" d2p1:ShipperApptRequestID="4490660303D5" d2p1:SchedulerCRID="234234" xmlns:d2p1="http://idealliance.org/Specs/mailxml12.0a/mailxml_defs" xmlns="http://idealliance.org/Specs/mailxml12.0a/mailxml_tm">
<SubmittingParty d2p1:MailerID6="123446" d2p1:CRID="342343" d2p1:MaildatUserLicense="A123" />
<SubmittingSoftware d2p1:SoftwareName="asds" d2p1:Vendor="123" d2p1:Version="12" />
<SubmitterTrackingID>2CAD3F71B4405EB16392</SubmitterTrackingID>
<DestinationEntry>No</DestinationEntry>
<OneTimeAppt>
<PreferredAppt>2012-06-29T09:00:00Z</PreferredAppt>
</OneTimeAppt>
<TrailerInfo>
<Trailer>
<TrailerNumber>A</TrailerNumber>
<TrailerLength>20ft</TrailerLength>
</Trailer>
<Carrier>
<CarrierName>N/A</CarrierName>
<URL>http://test.com</URL>
</Carrier>
<BillOfLadingNumber>N/A</BillOfLadingNumber>
</TrailerInfo>
</DeliveryApptCreateRequest>
после желаемого метода его следует изменить на все имя элемента, которое не имеет префикса с mailxml:
.
Как DeliveryApptCreateRequest
должно стать mailxml:DeliveryApptCreateRequest
в то время как элемент, как d2p1:CompanyName
должно оставаться как есть.
Я попытался со следующим кодом
private void RepalceFile(string xmlfile)
{
XmlDocument doc = new XmlDocument();
doc.Load(xmlfile);
var a = doc.CreateAttribute("xmlns:mailxml12tm");
a.Value = "http://idealliance.org/Specs/mailxml12.0a/mailxml_tm";
doc.DocumentElement.Attributes.Append(a);
doc.DocumentElement.Prefix = "mailxml12tm";
foreach (XmlNode item in doc.SelectNodes("//*"))
{
if (item.Prefix.Length == 0)
item.Prefix = "mailxml12tm";
}
doc.Save(xmlfile);
}
единственная проблема в том, что корневой элемент остается таким, как есть, пока все изменяются, как мне нужно
3 ответов
вы можете просто разобрать весь XML как строку и вставить пространства имен, где это необходимо. Однако это решение может создавать множество новых строк, используемых только в алгоритме, что не очень хорошо для производительности. Тем не менее, я написал функцию, анализирующую ее таким образом, и она, похоже, работает довольно быстро для образца XML, который вы опубликовали ;). Я могу отправить его, если вы хотите его использовать.
другое решение-загрузка XML как XmlDocument и воспользовавшись на самом деле это древовидная структура. Таким образом, можно создать метод рекурсивного добавления соответствующих пространств имен, где это необходимо.
К сожалению, XmlNode.Name
атрибут доступен только для чтения, поэтому вам нужно вручную скопировать всю структуру xml, чтобы изменить имена некоторых узлов.
У меня нет времени писать код, поэтому я просто позволил тебе писать. Если у вас возникнут какие-либо вопросы, просто дайте мне знать.
обновление
Я проверил ваш код и код, предложенный Джефф Меркадо и оба они, похоже, работают правильно, по крайней мере, в образце XML, который вы разместили в вопросе. Убедитесь, что XML, который вы пытаетесь проанализировать, совпадает с тем, который вы опубликовали.
просто чтобы заставить его работать и решить проблему добавления пространства имен, вы можете использовать код, который обрабатывает весь XML как String
и анализирует его вручную:
private static String UpdateNodesWithDefaultNamespace(String xml, String defaultNamespace)
{
if (!String.IsNullOrEmpty(xml) && !String.IsNullOrEmpty(defaultNamespace))
{
int currentIndex = 0;
while (currentIndex != -1)
{
//find index of tag opening character
int tagOpenIndex = xml.IndexOf('<', currentIndex);
//no more tag openings are found
if (tagOpenIndex == -1)
{
break;
}
//if it's a closing tag
if (xml[tagOpenIndex + 1] == '/')
{
currentIndex = tagOpenIndex + 1;
}
else
{
currentIndex = tagOpenIndex;
}
//find corresponding tag closing character
int tagCloseIndex = xml.IndexOf('>', tagOpenIndex);
if (tagCloseIndex <= tagOpenIndex)
{
throw new Exception("Invalid XML file.");
}
//look for a colon within currently processed tag
String currentTagSubstring = xml.Substring(tagOpenIndex, tagCloseIndex - tagOpenIndex);
int firstSpaceIndex = currentTagSubstring.IndexOf(' ');
int nameSpaceColonIndex;
//if space was found
if (firstSpaceIndex != -1)
{
//look for namespace colon between tag open character and the first space character
nameSpaceColonIndex = currentTagSubstring.IndexOf(':', 0, firstSpaceIndex);
}
else
{
//look for namespace colon between tag open character and tag close character
nameSpaceColonIndex = currentTagSubstring.IndexOf(':');
}
//if there is no namespace
if (nameSpaceColonIndex == -1)
{
//insert namespace after tag opening characters '<' or '</'
xml = xml.Insert(currentIndex + 1, String.Format("{0}:", defaultNamespace));
}
//look for next tags after current tag closing character
currentIndex = tagCloseIndex;
}
}
return xml;
}
вы можете проверить этот код, чтобы сделать приложение работает, однако я настоятельно рекомендую вам определить, почему другие предложенные решения не сработали.
поскольку в этом случае у вас определено пространство имен по умолчанию, вы можете просто удалить объявление пространства имен по умолчанию и добавить новое объявление для нового префикса, используя старое имя пространства имен, эффективно заменив его.
var prefix = "mailxml";
var content = XElement.Parse(xmlStr);
var defns = content.GetDefaultNamespace();
content.Attribute("xmlns").Remove();
content.Add(new XAttribute(XNamespace.Xmlns + prefix, defns.NamespaceName));
@JeffMercado решение не работает для меня (вероятно, так как у меня не было пространства имен по умолчанию).
Я закончил использование:
XNamespace ns = Constants.Namespace;
el.Name = (ns + el.Name.LocalName) as XName;
изменить пространство имен всего документа я использовал:
private void rewriteNamespace(XElement el)
{
// Change namespace
XNamespace ns = Constants.Namespace;
el.Name = (ns + el.Name.LocalName) as XName;
if (!el.HasElements)
return;
foreach (XElement d in el.Elements())
rewriteNamespace(d);
}
использование:
var doc = XDocument.parse(xmlStr);
rewriteNamespace(doc.Root)
HTH