草庐IT

C#:将对象呈现为 XML

coder 2024-06-24 原文

我正在寻找一种将对象树转换为 XML 的方法。写起来会很有趣,但我相信已经有人写过了。这是我的愿望 list :

  • 它不应该关心构造函数
  • 它应该理想地处理循环引用(不要太在意怎么做)
  • 它不应该要求对对象进行更改——例如,没有自定义属性
  • 它不应该关心或需要已知类型(例如,XmlInclude)
  • XML 应该非常简单 - 它需要运营团队成员可以阅读
  • 如果一个属性不能被序列化,它应该抑制错误并继续
  • 可以处理列表和字典

我不需要重建对象模型,所以只写解决方案很好(可能是预期的)。

我认为折扣:

  • XmlSerializer - 需要无参数构造函数,不支持循环引用
  • DataContractSerializer - 需要属性(选择加入)

最佳答案

罗伯特·罗斯尼 (Robert Rossney) 的帖子让我觉得这可能比我想象的要简单。所以这是一个非常粗略的尝试。它处理以下内容:

  • 如果无法读取属性,则打印异常作为值
  • 循环引用和多次出现。它为每个元素关联一个 ID;如果一个元素出现两次,它只是指向 ref ID。 Ref ID 对于对象图是唯一的(我可能应该使用 GUID,但这适合我的目的)。
  • 派生类型没有问题
  • 它不需要属性或特定的构造函数或其他废话
  • 它可以处理只读属性

这是一个输出示例(在我的测试对象中,订单上的“货币”产品抛出异常)。

<Customer Ref="1">
  <FirstName>Paul</FirstName>
  <LastName>Stovell</LastName>
  <FullName>Paul Stovell</FullName>
  <Orders>
    <Order Ref="2">
      <SKU>Apples</SKU>
      <Price>27.30</Price>
      <Currency>Something bad happened</Currency>
      <Customer Ref="1" />
    </Order>
    <Order Ref="3">
      <SKU>Pears</SKU>
      <Price>17.85</Price>
      <Currency>Something bad happened</Currency>
      <Customer Ref="1" />
    </Order>
    <Order Ref="2" />
  </Orders>
</Customer>

下面是示例对象模型和用法:

static void Main(string[] args)
{
    var customer = new Customer();
    customer.FirstName = "Paul";
    customer.LastName = "Stovell";
    customer.Orders.Add(new Order(customer) { Price = 27.30M, SKU = "Apples"});
    customer.Orders.Add(new Order(customer) { Price = 17.85M, SKU = "Pears"});
    customer.Orders.Add(customer.Orders[0]);

    var output = new StringWriter();
    var writer = new XmlTextWriter(output);
    writer.Formatting = Formatting.Indented;
    WriteComplexObject("Customer", customer, writer);
    Console.WriteLine(output.ToString());
    Console.ReadKey();
}

class Customer
{
    private readonly List<Order> _orders = new List<Order>();

    public Customer()
    {
    }

    public string FirstName { get; set; }
    public string LastName { get; set; }

    public string FullName
    {
        // Read-only property test
        get { return FirstName + " " + LastName; }
    }

    public List<Order> Orders
    {
        // Collections test
        get { return _orders; }
    }
}

class Order
{
    private readonly Customer _customer;

    public Order(Customer customer)
    {
        _customer = customer;
    }

    public string SKU { get; set; }
    public decimal Price { get; set; }
    public string Currency
    {
        // A proprty that, for some reason, can't be read
        get
        {
            throw new Exception("Something bad happened");
        }
    }

    public Customer Customer
    {
        get { return _customer; }
    }
}

实现如下:

public static void WriteObject(string name, object target, XmlWriter writer)
{
    WriteObject(name, target, writer, new List<object>(), 0, 10, -1);
}

private static void WriteObject(string name, object target, XmlWriter writer, List<object> recurringObjects, int depth, int maxDepth, int maxListLength)
{
    var formatted = TryToFormatPropertyValueAsString(target);
    if (formatted != null)
    {
        WriteSimpleProperty(name, formatted, writer);
    }
    else if (target is IEnumerable)
    {
        WriteCollectionProperty(name, (IEnumerable)target, writer, depth, maxDepth, recurringObjects, maxListLength);
    }
    else
    {
        WriteComplexObject(name, target, writer, recurringObjects, depth, maxDepth, maxListLength);
    }
}

private static void WriteComplexObject(string name, object target, XmlWriter writer, List<object> recurringObjects, int depth, int maxDepth, int maxListLength)
{
    if (target == null || depth >= maxDepth) return;
    if (recurringObjects.Contains(target))
    {
        writer.WriteStartElement(name);
        writer.WriteAttributeString("Ref", (recurringObjects.IndexOf(target) + 1).ToString());
        writer.WriteEndElement();
        return;
    }
    recurringObjects.Add(target);

    writer.WriteStartElement(name);
    writer.WriteAttributeString("Ref", (recurringObjects.IndexOf(target) + 1).ToString());
    foreach (var property in target.GetType().GetProperties())
    {
        var propertyValue = ReadPropertyValue(target, property);
        WriteObject(property.Name, propertyValue, writer, recurringObjects, depth + 1, maxDepth, maxListLength);
    }
    writer.WriteEndElement();
}

private static object ReadPropertyValue(object target, PropertyInfo property)
{
    try { return property.GetValue(target, null); }
    catch (Exception ex) { return ReadExceptionMessage(ex); }
}

private static string ReadExceptionMessage(Exception ex)
{
    if (ex is TargetInvocationException && ex.InnerException != null)
        return ReadExceptionMessage(ex.InnerException);
    return ex.Message;
}

private static string TryToFormatPropertyValueAsString(object propertyValue)
{
    var formattedPropertyValue = null as string;
    if (propertyValue == null)
    {
        formattedPropertyValue = string.Empty;
    }
    else if (propertyValue is string || propertyValue is IFormattable || propertyValue.GetType().IsPrimitive)
    {
        formattedPropertyValue = propertyValue.ToString();
    }
    return formattedPropertyValue;
}

private static void WriteSimpleProperty(string name, string formattedPropertyValue, XmlWriter writer)
{
    writer.WriteStartElement(name);
    writer.WriteValue(formattedPropertyValue);
    writer.WriteEndElement();
}

private static void WriteCollectionProperty(string name, IEnumerable collection, XmlWriter writer, int depth, int maxDepth, List<object> recurringObjects, int maxListLength)
{
    writer.WriteStartElement(name);
    var enumerator = null as IEnumerator;
    try
    {
        enumerator = collection.GetEnumerator();
        for (var i = 0; enumerator.MoveNext() && (i < maxListLength || maxListLength == -1); i++)
        {
            if (enumerator.Current == null) continue;
            WriteComplexObject(enumerator.Current.GetType().Name, enumerator.Current, writer, recurringObjects, depth + 1, maxDepth, maxListLength);
        }
    }
    catch (Exception ex)
    {
        writer.WriteElementString(ex.GetType().Name, ReadExceptionMessage(ex));
    }
    finally
    {
        var disposable = enumerator as IDisposable;
        if (disposable != null)
        {
            disposable.Dispose();
        }
        writer.WriteEndElement();
    }
}

我仍然有兴趣知道是否有更多久经考验的解决方案。

关于C#:将对象呈现为 XML,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/578729/

有关C#:将对象呈现为 XML的更多相关文章

  1. ruby-on-rails - 如何从 format.xml 中删除 <hash></hash> - 2

    我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为

  2. c# - 如何在 ruby​​ 中调用 C# dll? - 2

    如何在ruby​​中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL

  3. C# 到 Ruby sha1 base64 编码 - 2

    我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha

  4. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  5. ruby - Hanami link_to 助手只呈现最后一个元素 - 2

    我是HanamiWorld的新人。我已经写了这段代码:moduleWeb::Views::HomeclassIndexincludeWeb::ViewincludeHanami::Helpers::HtmlHelperdeftitlehtml.headerdoh1'Testsearchengine',id:'title'hrdiv(id:'test')dolink_to('Home',"/",class:'mnu_orizontal')link_to('About',"/",class:'mnu_orizontal')endendendendend我在模板上调用了title方法。htm

  6. ruby - 将对象设置为 nil 是否很常见? - 2

    我正在构建一个应用程序,想知道是否将未使用的对象设置为nil是生产级编码中的常见做法。我知道这只是垃圾收集器的提示,并不总是处理对象。 最佳答案 根据这个thread如果您使用完一个成员对象,将其设置为nil将引发被引用对象被垃圾回收。如果它是局部变量,方法exit将做同样的事情。也就是说,如果您要求将成员显式设置为nil,我会质疑您的设计。 关于ruby-将对象设置为nil是否很常见?,我们在StackOverflow上找到一个类似的问题: https://

  7. Ruby 将对象插入现有的已排序对象数组 - 2

    我有以下现有的Dog对象数组,它们按age属性排序:classDogattr_accessor:agedefinitialize(age)@age=ageendenddogs=[Dog.new(1),Dog.new(4),Dog.new(10)]我现在想插入一条新的狗记录,并将它放在数组中的正确位置。假设我想插入这个对象:another_dog=Dog.new(8)我想把它插入到数组中,让它成为数组中的第三项。这是一个人为的示例,旨在演示我特别想如何将一个项目插入到现有的有序数组中。我意识到我可以创建一个全新的数组并重新对所有对象进行排序,但这不是我的目标。谢谢!

  8. ruby-on-rails - 如何在 Rails 3 中禁用 XML 解析 - 2

    我想禁用HTTP参数的自动XML解析。但我发现命令仅适用于Rails2.x,它们都不适用于3.0:config.action_controller.param_parsers.deleteMime::XML(application.rb)ActionController::Base.param_parsers.deleteMime::XMLRails3.0中的等价物是什么? 最佳答案 根据CVE-2013-0156的最新安全公告你可以将它用于Rails3.0。3.1和3.2ActionDispatch::ParamsParser::

  9. c# - C# 中的 Flatten Ruby 方法 - 2

    我如何做Ruby方法"Flatten"RubyMethod在C#中。此方法将锯齿状数组展平为一维数组。例如:s=[1,2,3]#=>[1,2,3]t=[4,5,6,[7,8]]#=>[4,5,6,[7,8]]a=[s,t,9,10]#=>[[1,2,3],[4,5,6,[7,8]],9,10]a.flatten#=>[1,2,3,4,5,6,7,8,9,10 最佳答案 递归解决方案:IEnumerableFlatten(IEnumerablearray){foreach(variteminarray){if(itemisIEnume

  10. ruby - 可以像在 C# 中使用#region 一样在 Ruby 中使用 begin/end 吗? - 2

    我最近从C#转向了Ruby,我发现自己无法制作可折叠的标记代码区域。我只是想到做这种事情应该没问题:classExamplebegin#agroupofmethodsdefmethod1..enddefmethod2..endenddefmethod3..endend...但是这样做真的可以吗?method1和method2最终与method3是同一种东西吗?还是有一些我还没有见过的用于执行此操作的Ruby惯用语? 最佳答案 正如其他人所说,这不会改变方法定义。但是,如果要标记方法组,为什么不使用Ruby语义来标记它们呢?您可以使用

随机推荐