我正在尝试使用System.Security.Cryptography.Xml.XMLDsigC14nTransform
类c#.net Framework 2.0来规范化xml节点。
该实例需要三种不同的输入类型:NodeList,Stream和XMLDocument。我尝试使用所有这些输入类型的转换,但我得到不同的结果。 我真正想要做的是规范化单个节点,但正如您在输出文件中看到的那样,输出不包含任何内部xml。
非常感谢有关规范化XML节点的正确方法的任何建议。 最好,
string path = @"D:\Test\xml imza\sign.xml";
XmlDocument xDoc = new XmlDocument();
xDoc.PreserveWhitespace = true;
using (FileStream fs = new FileStream(path, FileMode.Open))
{
xDoc.Load(fs);
}
// canon node list
XmlNodeList nodeList = xDoc.SelectNodes("//Child1");
XmlDsigC14NTransform transform = new XmlDsigC14NTransform();
transform.LoadInput(nodeList);
MemoryStream ms = (MemoryStream)transform.GetOutput(typeof(Stream));
File.WriteAllBytes(@"D:\Test\xml imza\child1.xml", ms.ToArray());
// canon XMLDocument
transform = new XmlDsigC14NTransform();
transform.LoadInput(xDoc);
ms = (MemoryStream)transform.GetOutput(typeof(Stream));
File.WriteAllBytes(@"D:\Test\xml imza\doc.xml", ms.ToArray());
// Document to Stream
ms = new MemoryStream();
XmlWriter xw = XmlWriter.Create(ms);
xDoc.WriteTo(xw);
xw.Flush();
ms.Position = 0;
transform = new XmlDsigC14NTransform();
transform.LoadInput(ms);
ms = (MemoryStream)transform.GetOutput(typeof(Stream));
File.WriteAllBytes(@"D:\Test\xml imza\ms.xml", ms.ToArray());
// node to stream
ms = new MemoryStream();
xw = XmlWriter.Create(ms);
nodeList[0].WriteTo(xw);
xw.Flush();
ms.Position = 0;
transform = new XmlDsigC14NTransform();
transform.LoadInput(ms);
ms = (MemoryStream)transform.GetOutput(typeof(Stream));
File.WriteAllBytes(@"D:\Test\xml imza\ms2.xml", ms.ToArray());
sign.xml
<?xml version="1.0" encoding="utf-8" ?>
<Root Attr="root" xmlns:test="http://www.test.com/xades#">
<Child1 Cttribute="c3" Attribute1="c1" Bttribute="c2">
<child11 Attribute11="c11">Element11</child11>
</Child1>
<Child2 Attribute2="c2">
<child21 Attribute21="c21">Element21</child21>
<child22 Attribute22="c22">Element22</child22>
</Child2>
<Child3 Attribute3="c3">
<child31 Attribute32="c31">
<child311 Attribute311="c311">Element311</child311>
</child31>
</Child3>
</Root>
Child1.xml
<Child1 xmlns:test="http://www.test.com/xades#"></Child1>
doc.xml
<Root xmlns:test="http://www.test.com/xades#" Attr="root">
<Child1 Attribute1="c1" Bttribute="c2" Cttribute="c3">
<child11 Attribute11="c11">Element11</child11>
</Child1>
<Child2 Attribute2="c2">
<child21 Attribute21="c21">Element21</child21>
<child22 Attribute22="c22">Element22</child22>
</Child2>
<Child3 Attribute3="c3">
<child31 Attribute32="c31">
<child311 Attribute311="c311">Element311</child311>
</child31>
</Child3> 
</Root>
ms.xml
<Root xmlns:test="http://www.test.com/xades#" Attr="root">
<Child1 Attribute1="c1" Bttribute="c2" Cttribute="c3">
<child11 Attribute11="c11">Element11</child11>
</Child1>
<Child2 Attribute2="c2">
<child21 Attribute21="c21">Element21</child21>
<child22 Attribute22="c22">Element22</child22>
</Child2>
<Child3 Attribute3="c3">
<child31 Attribute32="c31">
<child311 Attribute311="c311">Element311</child311>
</child31>
</Child3>
</Root>
ms2.xml
<Child1 Attribute1="c1" Bttribute="c2" Cttribute="c3">
<child11 Attribute11="c11">Element11</child11>
</Child1>
答案 0 :(得分:3)
我认为,你的答案在你的问题中,“我真正想做的是规范化单个节点,但正如你在输出文件中看到的那样,输出不包含任何内部xml。” / p>
如果我理解你,那么你真的不想要规范单个节点,或者你很高兴它不包含内部XML。您希望规范化单个子树。
XPath返回节点,而不是子树。默认情况下,XPath表达式返回的节点上的某些操作将包含其子节点和属性节点,但故意规范化不是其中之一,因为这些子节点中的某些节点可能会以您未签名的方式变为可变。在签名时,您只需准确签署您说要签名的节点。
更改代码中的行:
XmlNodeList nodeList = xDoc.SelectNodes("//Child1");
为:
XmlNodeList nodeList =
xDoc.SelectNodes("//Child1/descendant-or-self::node()|//Child1//@*");
表示我在child1.xml中获得以下内容:
<Child1 xmlns:test="http://www.test.com/xades#" Attribute1="c1" Bttribute="c2" Cttribute="c3">
<child11 Attribute11="c11">Element11</child11>
</Child1>
我认为这是你想要的吗?
顺便说一下,更精确的是:
XmlNodeList nodeList =
xDoc.SelectNodes("//Child1[1]/descendant-or-self::node()|//Child1[1]//@*");
可能很有用,因为xpath评估可以在到达第一个</Child1>
时停止,如果您的实际数据很大,性能增益可能很大。
答案 1 :(得分:1)
我可能在MSDN找到解决方案如果我正确地解决了问题。
这是否解决了这个问题?:
string path = @"sign.xml";
var xDoc = new XmlDocument();
xDoc.PreserveWhitespace = true;
using (var fs = new FileStream(path, FileMode.Open))
{
xDoc.Load(fs);
}
// canon node list
XmlNodeList nodeList = xDoc.SelectNodes("//Child1");
var transform = new XmlDsigC14NTransform(true)
{
Algorithm = SignedXml.XmlDsigExcC14NTransformUrl
};
var validInTypes = transform.InputTypes;
var inputType = nodeList.GetType();
if (!validInTypes.Any(t => t.IsAssignableFrom(inputType)))
{
throw new ArgumentException("Invalid Input");
}
transform.LoadInput(xDoc);
var innerTransform = new XmlDsigC14NTransform();
innerTransform.LoadInnerXml(xDoc.SelectNodes("//."));
var ms = (MemoryStream) transform.GetOutput(typeof (Stream));
ms.Flush();
File.WriteAllBytes(@"child1.xml", ms.ToArray());
在child1.xml中我有:
<Root xmlns:test="http://www.test.com/xades#" Attr="root">
<Child1 Attribute1="c1" Bttribute="c2" Cttribute="c3">
<child11 Attribute11="c11">Element11</child11>
</Child1>
<Child2 Attribute2="c2">
<child21 Attribute21="c21">Element21</child21>
<child22 Attribute22="c22">Element22</child22>
</Child2>
<Child3 Attribute3="c3">
<child31 Attribute32="c31">
<child311 Attribute311="c311">Element311</child311>
</child31>
</Child3>
</Root>
希望它有所帮助。 托拜厄斯
答案 2 :(得分:0)
你检查过MSDN:http://msdn.microsoft.com/en-us/library/fzh48tx1.aspx他们页面上的示例有一条评论,上面写着“此转换不包含内部XML元素” - 这意味着一个已知问题。
您可以尝试不同的XPath,例如// child1 / *或// child1 | // child1 / *或// child1 //或显式节点()选择(检查http://msdn.microsoft.com/en-us/library/ms256471.aspx处的完整XPath语法)但是你处于灰色地带 - 带着虫子赌博。
因此,在你的ms2.xml中你想要的实际输出你只需暂时进行中间序列化。
同时启动Reflector并查看 - 该课程可能并不十分复杂。
答案 3 :(得分:0)
关于如何处理XmlDocument不区分源(不应保留)中的U + 000D与源中的
等显式引用(应该保留)的事实的单独答案。
而不是:
using (FileStream fs = new FileStream(path, FileMode.Open))
{
xDoc.Load(fs);
}
我们首先创建一个新行清理TextReader:
private class LineCleaningTextReader : TextReader
{
private readonly TextReader _src;
public LineCleaningTextReader(TextReader src)
{
_src = src;
}
public override int Read()
{
int r = _src.Read();
switch(r)
{
case 0xD:// \r
switch(_src.Peek())
{
case 0xA: case 0x85: // \n or NEL char
_src.Read();
break;
}
return 0xA;
case 0x85://NEL
return 0xA;
default:
return r;
}
}
}
然后我们在加载xDoc中使用它:
using (FileStream fs = new FileStream(path, FileMode.Open))
{
using(TextReader tr = new StreamReader(fs))
xDoc.Load(new LineCleaningTextReader(tr));
}
然后在处理之前将新行标准化,但保留显式 单独