【问题标题】:Create XML fragment without xmlns namespace declaration创建没有 xmlns 命名空间声明的 XML 片段
【发布时间】:2016-08-22 09:30:19
【问题描述】:

我正在尝试通过代码创建以下 XML:

<log4j:event logger="MyTools" level="WARN" timestamp="763">
  <log4j:message>This is a log message.</log4j:message>
</log4j:event>

但我无法摆脱添加的xmlns:log4j="http://my-project.org/log4j/"。目前我正在测试以下代码(见HERE):

XNamespace ns = "http://my-project.org/log4j/";
var root = new XElement("root", new XAttribute(XNamespace.Xmlns + "log4j", ns));
var eventElement = new XElement(ns + "event");
root.Add(eventElement);
    
eventElement.SetAttributeValue("logger", "MyTools");
eventElement.SetAttributeValue("level", "WARN");
eventElement.SetAttributeValue("timestamp", DateTime.Now.Millisecond);
    
eventElement.SetElementValue(ns + "message", "This is a log message.");

当我将 eventElement 转换为字符串时,我将命名空间添加到 log4j:event 节点,而在转换 root 时,它仅添加到根元素(因为它应该是有效 XML 的 IMO),但是 如何在没有命名空间声明的情况下仅获取事件节点作为 XML 片段/节点? 我也尝试过使用 XmlWriter,但结果相同。
我也对其他方法持开放态度。

目前我使用String.Replace(" xmlns:log4j=\"" + ns + "\"", string.Empty) 来摆脱它,但这相当慢(使完整的方法慢了约 50%),因为这可能以高频率发生(=记录器)我想做尽可能快。
我需要它而不需要命名空间声明的原因是一些日志侦听器不喜欢命名空间并且如果它存在则崩溃(日志通过 UDP 发送到日志侦听器)。

我想要改进的是以下 NLog 渲染器:
https://github.com/NLog/NLog/blob/master/src/NLog/LayoutRenderers/Log4JXmlEventLayoutRenderer.cs
除了缓慢的Replace(参见Ln 300)之外,我还替换了调用站点/UserStackFrame 以使用[CallerMemberName] 参数以及由我自己的日志包装器实现的此类参数。我可以将测试系统上 >100ms/1k 日志的日志生成速度加快到 ~35ms/1k,只要将命名空间留在那里(因为它应该是有效的 XML),但正如已经说过的一些日志侦听器(即 Sentinel)当它在那里时崩溃......

【问题讨论】:

  • 你想要得到的是notXml。您不应该期望使用 XML 的工具会生成其他不兼容的东西。如果您的工具无法正确处理格式良好的 XML,那么您应该寻求修复这些工具。命名空间前缀,它们自己没有语义。只有当命名空间前缀与命名空间声明相关联时,您才能获得正确的语义。
  • @Damien_The_Unbeliever:我知道它“本身”是无效的 XML,这就是为什么我称它为“XML 片段”。事实是我既不能改变 XML 结构,也不能改变用来收听它们的工具,因为两者都超出了我的控制范围,所以我正在尽我所能让它们尽快一起工作......
  • 括号放错地方了。您将 'ns' 添加到 Attribute 而不是 Element : new XElement("root", new XAttribute(XNamespace.Xmlns + "log4j"), ns);

标签: c# xml linq-to-xml xelement xmlwriter


【解决方案1】:

您展示的 sn-p 在没有命名空间的 XML 世界中格式正确,如果您确实需要使用 .NET 创建这样的 sn-p,那么旧版 XmlTextWriter 允许如下:

        using (StringWriter sw = new StringWriter())
        {

            using (XmlTextWriter xtw = new XmlTextWriter(sw))
            {
                xtw.Namespaces = false;
                xtw.Formatting = Formatting.Indented;
                xtw.WriteStartElement("log4j:event");
                xtw.WriteAttributeString("logger", "MyTools");
                xtw.WriteAttributeString("level", "WARN");
                xtw.WriteAttributeString("timestamp", "763");
                xtw.WriteElementString("log4j:message", "This is a log message.");
                xtw.WriteEndElement();
            }
            string result = sw.ToString();
            Console.WriteLine(result);
        }

结果:

<log4j:event logger="MyTools" level="WARN" timestamp="763">
  <log4j:message>This is a log message.</log4j:message>
</log4j:event>

但是请注意,现在大多数 XML 解析器和 API 都期望命名空间格式良好的 XML(其中只有冒号之前的前缀绑定到命名空间时才允许使用带有冒号的名称),因此创建的 sn-p 不能例如使用 LINQ to XML 处理或使用 XmlReader.Create 创建的默认 XmlReader。

【讨论】:

  • 谢谢,这正是我所需要的 - 甚至比其他两种方法快一点(现在 ~30ms / 1k 日志)。
猜你喜欢
  • 1970-01-01
  • 2013-10-26
  • 2016-04-24
  • 1970-01-01
  • 2011-08-08
  • 2014-03-23
  • 2013-01-10
  • 2010-12-07
  • 1970-01-01
相关资源
最近更新 更多