【发布时间】:2017-09-25 23:54:31
【问题描述】:
我不得不重新创建供应商的 XML 文件。我无权访问他们的代码、架构或任何东西,所以我使用XmlSerializer 和属性来执行此操作。我这样做是因为系统使用了一个通用的XmlWriter,我为编写其他系统XML文件而构建,所以我用一块石头杀死了两只鸟。一切都很好,除了一个财产价值。供应商 XML 如下所示:
<TextOutlTxt>
<p style="text-align:left;margin-top:0pt;margin-bottom:0pt;">
<span>SUBSTA SF6 CIRCUIT BKR CONC FDN "C"</span>
</p>
</TextOutlTxt>
这是我的属性配置:
private string _value;
[XmlElement("TextOutlTxt")]
public XmlNode Value
{
get
{
string text = _value;
text = Regex.Replace(text, @"[\a\b\f\n\r\t\v\\""'&<>]", m => string.Join(string.Empty, m.Value.Select(c => string.Format("&#x{0:X};", Convert.ToInt32(c))).ToArray()));
string value = "\n<p style=\"text-align:left;margin-top:0pt;margin-bottom:0pt;\">\n<span>ReplaceMe</span>\n</p>\n";
XmlDocument document = new XmlDocument();
document.InnerXml = "<root>" + value + "</root>";
XmlNode innerNode = document.DocumentElement.FirstChild;
innerNode.InnerText = text;
return innerNode;
}
set
{ }
}
这给了我:
<TextOutlTxt>
<p style="text-align:left;margin-top:0pt;margin-bottom:0pt;" xmlns="">SUBSTA SF6 CIRCUIT BKR CONC FDN &#x22;C&#x22;</p>
</TextOutlTxt>
所以我很接近,但没有雪茄。有一个不需要的xmlns="..." 属性;它不能存在。在我的XmlWriter 中,我已执行以下操作来删除命名空间,除非在它正在序列化的对象顶部找到:
protected override void OnWrite<T>(T sourceData, Stream outputStream)
{
IKnownTypesLocator knownTypesLocator = KnownTypesLocator.Instance;
//Let's see if we can get the default namespace
XmlRootAttribute xmlRootAttribute = sourceData.GetType().GetCustomAttributes<XmlRootAttribute>().FirstOrDefault();
XmlSerializer serializer = null;
if (xmlRootAttribute != null)
{
string nameSpace = xmlRootAttribute.Namespace ?? string.Empty;
XmlSerializerNamespaces nameSpaces = new XmlSerializerNamespaces();
nameSpaces.Add(string.Empty, nameSpace);
serializer = new XmlSerializer(typeof(T), new XmlAttributeOverrides(), knownTypesLocator.XmlItems.ToArray(), xmlRootAttribute, nameSpace);
//Now we can serialize
using (StreamWriter writer = new StreamWriter(outputStream))
{
serializer.Serialize(writer, sourceData, nameSpaces);
}
}
else
{
serializer = new XmlSerializer(typeof(T), knownTypesLocator.XmlItems.ToArray());
//Now we can serialize
using (StreamWriter writer = new StreamWriter(outputStream))
{
serializer.Serialize(writer, sourceData);
}
}
}
我确定我忽略了一些东西。任何帮助将不胜感激!
2017 年 9 月 26 日更新 所以......我被要求提供更多细节,特别是对我的代码目的的解释,以及一个可重现的例子。所以这两个都是:
- XML 的用途。我正在编写两个系统之间的界面 UI。我从第一个系统读取数据,为用户提供处理数据的选项,然后提供将数据导出到第二个系统可以导入的文件的能力。它涉及物料清单系统,其中系统一是 CAD 图纸和这些图纸中的对象,系统二是企业估算系统,它也被配置为支持电子物料清单。供应商给了我重新创建的 XML。
-
功能齐全的示例代码....我尝试以可重现的形式概括代码。
[XmlRoot("OutlTxt", Namespace = "http://www.mynamespace/09262017")] public class OutlineText { private string _value; [XmlElement("TextOutlTxt")] public XmlNode Value { get { string text = _value; text = Regex.Replace(text, @"[\a\b\f\n\r\t\v\\""'&<>]", m => string.Join(string.Empty, m.Value.Select(c => string.Format("&#x{0:X};", Convert.ToInt32(c))).ToArray())); string value = "\n<p style=\"text-align:left;margin-top:0pt;margin-bottom:0pt;\">\n<span>ReplaceMe</span>\n</p>\n"; XmlDocument document = new XmlDocument(); document.InnerXml = "<root>" + value + "</root>"; XmlNode innerNode = document.DocumentElement.FirstChild; innerNode.InnerText = text; return innerNode; } set { } } private OutlineText() { } public OutlineText(string text) { _value = text; } } public class XmlFileWriter { public void Write<T>(T sourceData, FileInfo targetFile) where T : class { //This is actually retrieved through a locator object, but surely no one will mind an empty //collection for the sake of an example Type[] knownTypes = new Type[] { }; using (FileStream targetStream = targetFile.OpenWrite()) { //Let's see if we can get the default namespace XmlRootAttribute xmlRootAttribute = sourceData.GetType().GetCustomAttributes<XmlRootAttribute>().FirstOrDefault(); XmlSerializer serializer = null; if (xmlRootAttribute != null) { string nameSpace = xmlRootAttribute.Namespace ?? string.Empty; XmlSerializerNamespaces nameSpaces = new XmlSerializerNamespaces(); nameSpaces.Add(string.Empty, nameSpace); serializer = new XmlSerializer(typeof(T), new XmlAttributeOverrides(), knownTypes, xmlRootAttribute, nameSpace); //Now we can serialize using (StreamWriter writer = new StreamWriter(targetStream)) { serializer.Serialize(writer, sourceData, nameSpaces); } } else { serializer = new XmlSerializer(typeof(T), knownTypes); //Now we can serialize using (StreamWriter writer = new StreamWriter(targetStream)) { serializer.Serialize(writer, sourceData); } } } } } public static void Main() { OutlineText outlineText = new OutlineText(@"SUBSTA SF6 CIRCUIT BKR CONC FDN ""C"""); XmlFileWriter fileWriter = new XmlFileWriter(); fileWriter.Write<OutlineText>(outlineText, new FileInfo(@"C:\MyDirectory\MyXml.xml")); Console.ReadLine(); }
产生的结果:
<?xml version="1.0" encoding="utf-8"?>
<OutlTxt xmlns="http://www.mynamespace/09262017">
<TextOutlTxt>
<p style="text-align:left;margin-top:0pt;margin-bottom:0pt;" xmlns="">SUBSTA SF6 CIRCUIT BKR CONC FDN &#x22;C&#x22;</p>
</TextOutlTxt>
</OutlTxt>
2017 年 9 月 27 日编辑 根据以下解决方案中的请求,我遇到的第二个问题是保留十六进制代码。根据上面的例子来说明这个问题,假设之间的值是
SUBSTA SF6 CIRCUIT BKR CONC FDN "C"
供应商文件期望文字采用十六进制代码格式,如下所示
SUBSTA SF6 CIRCUIT BKR CONC FDN "C"
我已将示例代码的 Value 属性重新排列为:
private string _value;
[XmlAnyElement("TextOutlTxt", Namespace = "http://www.mynamespace/09262017")]
public XElement Value
{
get
{
string value = string.Format("<p xmlns=\"{0}\" style=\"text-align:left;margin-top:0pt;margin-bottom:0pt;\"><span>{1}</span></p>", "http://www.mynamespace/09262017", _value);
string innerXml = string.Format("<TextOutlTxt xmlns=\"{0}\">{1}</TextOutlTxt>", "http://www.mynamespace/09262017", value);
XElement element = XElement.Parse(innerXml);
//Remove redundant xmlns attributes
foreach (XElement descendant in element.DescendantsAndSelf())
{
descendant.Attributes().Where(att => att.IsNamespaceDeclaration && att.Value == "http://www.mynamespace/09262017").Remove();
}
return element;
}
set
{
_value = value == null ? null : value.ToString();
}
}
如果我使用代码
string text = Regex.Replace(element.Value, @"[\a\b\f\n\r\t\v\\""'&<>]", m => string.Join(string.Empty, m.Value.Select(c => string.Format("&#x{0:X};", Convert.ToInt32(c))).ToArray()));
要在 XElement.Parse() 之前创建十六进制代码值,XElement 会将它们转换回其文字值。如果我尝试在 XElement.Parse() 之后直接设置 XElement.Value(或通过 SetValue()),它会将 " 更改为 " 不仅如此,而且它似乎会混淆元素输出并添加额外的元素把它搞砸了。
编辑 9/27/2017 #2 澄清一下,原来的实现有一个相关的问题,即转义文本被重新转义。 IE。我得到了
SUBSTA SF6 CIRCUIT BKR CONC FDN &#x22;C&#x22;
但是想要
SUBSTA SF6 CIRCUIT BKR CONC FDN "C"
【问题讨论】:
-
您可能完全过度设计了问题,当我尝试您的代码时,我通过访问 OuterXml 属性摆脱了另一端
"<p style="text-align:left;margin-top:0pt;margin-bottom:0pt;">SUBSTA SF6 CIRCUIT BKR CONC FDN &amp;#x26;amp;#x22;C&amp;#x26;amp;#x22;</p>"。当您说重新创建供应商 XML 文件时,您的意思是这样您就可以提出请求吗?或者这样你就可以拦截响应?不够清楚。 -
您的问题不清楚,因为您没有展示如何初始化
_value或您的容器类型是什么样的。但这是你需要的吗? dotnetfiddle.net/VtdFky 如果你提供了minimal reproducible example,我将能够明确回答。 -
您的代码是 html 而不是 xml。该文件已针对 html 正确编码。参见维基:en.wikipedia.org/wiki/…。您可以使用 System.Net.WebUtility.HtmlDecode() 和 System.Net.WebUtility.HtmlEncode()
-
@jdweng 问题中的文档是有效的 XML。此外,我不知道您认为
HtmlDecode和HtmlEncode会做什么,但我敢打赌,您不知道他们在做什么,或者您根本不理解这个问题。跨度> -
@jdweng 对不起,但还是谢谢你。 HtmlEncode 非常适合将字符串编码为与 html 文本兼容,但这不是这里的问题。我已经按照供应商期望的格式解决了这个问题。现在我只想删除作为结果创建的命名空间元素。
标签: c# xml xmlserializer xmldocument xmlnode