不使用 XmlDocument.Loadxml() 函数将 XML 反序列化为 JSON

Posted

技术标签:

【中文标题】不使用 XmlDocument.Loadxml() 函数将 XML 反序列化为 JSON【英文标题】:Deserializing XML into JSON without using XmlDocument.Loadxml() function 【发布时间】:2012-06-22 14:55:03 【问题描述】:

我有一个独特的问题。我正在将一个 dll 注册为 SQL Server 数据库中的一个程序集,该数据库接受一个 SQLXml 变量以及两个字符串,并将数据序列化为 JSON 格式。

作为参考,这里是方法调用:

[SqlProcedure]
public static void Receipt(SqlString initiatorPassword,
                           SqlString initiatorId,
                           SqlXml XMLOut,
                           out SqlString strMessge)

如果这是任何其他类型的应用程序,我会为此应用程序使用 Newtonsoft.Json 或 Jayrock。通常我会按照here 给出的答案做类似的事情:

XmlReader r = (XmlReader)XmlOut.CreateReader();
XmlDocument doc = new XmlDocument();
doc.load(r);

但是,由于我使用的是 SQLClr,因此有一定的规则。其中之一是 .Load() 和任何其他继承的方法都不能使用。我认为 .Net 框架说得最好:

System.InvalidOperationException:无法加载动态生成的序列化程序集。在某些托管环境中 装配负载功能受到限制, 考虑使用预生成的序列化程序。请参阅内部异常以获取更多信息。 ---> System.IO.FileLoadException: LoadFrom()、LoadFile()、Load(byte[]) 和 LoadModule() 已被主机禁用。

我无论如何都不会流利地使用 SqlClr,但如果我正确理解 this blog,这是由于 SqlCLR 的规则不允许 .Load() 和继承的方法而没有签名并具有强名称。我的 DLL 和我正在使用的第 3 方 DLL 没有强名称,我也不能自己重建和签名它们。因此,这让我无法在不使用负载的情况下尝试完成此任务(除非有人知道另一种方法)

我唯一能想到的解决方案是一个非常丑陋的 while 循环,它不能正常工作,我得到了一个“Jayrock.Json.JsonException:JSON 对象内的 JSON 成员值必须在其成员之前名称”例外。这是我编写的 while 循环(我知道,这不是我最好的代码):

 int lastdepth = -1;
 Boolean objend = true;
 Boolean wt = false;
//Write Member/Object statements for the header omitted
JsonWriter w = new JsonTextWriter()
 while (m.Read())
                
                    if ((lastdepth == -1) && (m.IsStartElement()))
                    //Checking for root element
                        lastdepth = 0;
                    
                    if ((m.IsStartElement()) && (lastdepth != -1))
                    //Checking for Start element ( <html> )
                        w.WriteMember(m.Name);
                        if (objend)
                         //Check if element is new Parent Node, if so, write start object
                            w.WriteStartObject();
                            objend = false;
                        
                    
                    if (m.NodeType == XmlNodeType.Text)
                     //Writes text here.  NOTE: m.Depth > lastdepth here!!!!!!!
                        w.WriteString(m.Value);
                        wt = true;
                    
                    if (m.NodeType == XmlNodeType.Whitespace) //If whitespace, keep on truckin
                     m.Skip(); 
                    if ((m.NodeType == XmlNodeType.EndElement) && (wt == false) && (lastdepth > m.Depth))
                    //End element that ends a series of "Child" nodes
                        w.WriteEndObject();
                        objend = true;
                    
                    if ((m.NodeType == XmlNodeType.EndElement) && (wt == true))//Standard end of an el
                     wt = false; 
                    lastdepth = m.Depth;
                
                w.WriteEndObject();
                jout = w.ToString();

我的问题是,由于我不能使用 .load() 并且我的 while 循环调试起来很麻烦,这里最好的方法是什么?通常讨论的另一种方法是反序列化为具有匹配变量的对象,但我有一个相当大的 XML 来自 SQL Server。我的循环是动态编程的一次尝试,因为有大约 200 个字段被拉出来制作这个 XML。

注意:我正在使用 Jayrock 并在 .Net Framework 2.0 中工作。我目前无法更改框架版本。

【问题讨论】:

AFAIK XmlDocument.LoadXml() 不需要领导动态程序集。您确定您没有尝试使用 XML 序列化吗?你能发布异常的堆栈跟踪吗? 【参考方案1】:

我希望这适用于您,但除非您尝试,否则不会知道。来自here:

EXTERNAL_ACCESS 解决代码需要访问的场景 服务器之外的资源,例如文件、网络、注册表和 环境变量。每当服务器访问外部 资源,它模拟用户调用的安全上下文 托管代码。

UNSAFE 代码权限适用于程序集处于 无法验证安全或需要额外访问受限的 资源,例如 Microsoft Win32 API。

我相信这两个中的一个是您需要的,可能是EXTERNAL_ACCESS。更多:

我们建议从程序集文件创建非对称密钥 在主数据库中。映射到此非对称密钥的登录必须 然后被创建,并且登录必须被授予外部访问权限 ASSEMBLY 或 UNSAFE ASSEMBLY 许可。

注意:您必须创建一个新登录名才能与非对称密钥关联。这 登录仅用于授予权限;它不一定是 与用户关联,或在应用程序中使用。

以及相关代码(我过去成功使用过):

USE master; 
GO  

CREATE ASYMMETRIC KEY SQLCLRTestKey FROM EXECUTABLE FILE = 'C:\MyDBApp\SQLCLRTest.dll'   
CREATE LOGIN SQLCLRTestLogin FROM ASYMMETRIC KEY SQLCLRTestKey   
GRANT EXTERNAL ACCESS ASSEMBLY TO SQLCLRTestLogin; 
GO 

我想您还希望在您的项目中启用生成序列化程序集

这实际上回避了您的问题,但这确实是允许其他 DLL 成功加载以便您编写正确代码的首选方式。

【讨论】:

啊!我希望我在因为这个错误而改变我的整个程序之前看到了这个。非常感谢,我相信我很快就会使用它!【参考方案2】:

Code For JayRock

您的代码抛出异常:

A JSON member value inside a JSON object must be preceded by its member name.

这个异常来自方法:

    private void EnsureMemberOnObjectBracket() 
    
        if (_state.Bracket == JsonWriterBracket.Object)
            throw new JsonException("A JSON member value inside a JSON 
             object must be preceded by its member name.");
    

来自该代码的包含调用来自:

    public sealed override void WriteString(string value)
    
        if (Depth == 0)
        
            WriteStartArray(); WriteString(value); WriteEndArray();
        
        else
        
            EnsureMemberOnObjectBracket();
            WriteStringImpl(value);
            OnValueWritten();
        
    

您的代码调用调用EnsureMemberOnObjectBracket 的方法的唯一一次是来自一个地方:

if (m.NodeType == XmlNodeType.Text)
 //Writes text here.  NOTE: m.Depth > lastdepth here!!!!!!!
 w.WriteString(m.Value);
 wt = true;

这意味着这里有错误。也许你可以在这里做一些 try/catch,或改进你的代码。

【讨论】:

以上是关于不使用 XmlDocument.Loadxml() 函数将 XML 反序列化为 JSON的主要内容,如果未能解决你的问题,请参考以下文章

试图解析 xml,但 xmldocument.loadxml() 正在尝试下载?

.NET XmlDocument LoadXML 和实体

为啥包含 XML 标头时 C# XmlDocument.LoadXml(string) 会失败?

原创XmlDocument.LoadXml和Load的区别

XmlDocument.selectNodes() and selectSingleNode()的xpath的学习资料

C语言 不允许使用不完整的类型