【问题标题】:Generic XML to Object mapping通用 XML 到对象的映射
【发布时间】:2013-06-16 17:27:10
【问题描述】:

我收到一条 xml 消息,将其转换为不同的格式,然后继续发送。消息的范围从大约 40 行到 600 行或更多行 xml,平均在 100 行左右。我每秒可以收到多条消息,但在繁忙时间平均每分钟大约 15 到 20 条。

由于 xml 为现有应用程序提供了新信息,因此我创建了一个模拟输出 xml 结构的类。该对象将创建输出 xml,仅包括已更改的项目,并将输入术语翻译成消费应用程序可以理解的语言。我难以弄清楚的是如何轻松地将传入的 xml 映射到对象。

传入的 xml 使用几个不同的模板来确定每个节点的格式。我正在尝试创建一个映射,该映射可以确定如果节点名为 n,那么它需要转到对象 m。下面是我正在尝试做的一个简化示例。

消息 1

<Incoming>
    <Name>Bob</Name>
    <City>Seattle</City>
    <Hobby>Fishing</Hobby>
</Incoming>

消息 2

<Incoming>
    <Name>Bob</Name>
    <Employment>
        <JobTitle>Sales</JobTitle>
        <Manager>Jessica</Manager>
    </Employment>
    <Hobby>Reading</Hobby>
</Incoming>

这将进入一个类似的对象:

public Customer
{
    public String Name{get; set;}
    public Address CustomerAddress{get;set;}
    public Employer CustomerEmployer{get;set;}
    public List<String> Hobbies{get;set;}
}

public Address
{
    public String StreetAddress{get;set;}
    public String City{get;set;}
    public String State{get;set;}
    public String Zip{get;set;}
}

public Employer
{
    public String Company{get;set;}
    public String Position{get;set;}
    public String Manager{get;set;}
    public Address CompanyAddress{get;set;}
}

如果不创建一个长的 Switch Case,有没有人知道如何最好地将信息从 xml 获取到对象?由于信息量很大,我更加注意处理的时间成本。

我考虑过制作一个映射;像

<Name>Customer:Name</Name>
<City>Customer:Address:City</City>

但是,如何映射列表中的项目存在问题,例如 Hobby。还有如何快速消费映射的问题。我唯一能想到的是让每个对象处理地图的一部分以确定路径,尽管这听起来很昂贵。

我并不担心不同级别的重复地址。这个数据就是一个例子。在我的实际 xml 中,我认为我没有任何重复项。

我感谢人们提供的任何帮助或想法。提前谢谢你。

【问题讨论】:

  • 内置 XML 序列化对您不起作用有什么原因吗?请参阅有关 System.Xml.Serialization 的 MSDN。 msdn.microsoft.com/en-us/library/system.xml.serialization.aspx
  • 您有 XML 的 XSD 吗?如果是这样,请将带有 System.Xml.Serialization 的 XSD 用作 @MarcL。建议的。 XSD 允许您指定必需和非必需字段等。然后您的对象将填充指定的字段,其他字段将为空。您还可以在传递对象时应用自定义规则来确定要序列化的内容和不序列化的内容。
  • 感谢您的回复。我不从对象序列化到传出 xml 的原因是我只包含已更改的值(或对象),以节省消费对象的处理。至于从传入的 xml 序列化,我试图保持转换类的通用性,因此它可以使用任意命名约定接受任何格式的 xml。这就是为什么我认为为每个应用程序使用映射 xml 转换器将使用的 xml 是一个好方法。事实证明这比我预期的要复杂一些。

标签: c# xml converter


【解决方案1】:

我能够通过映射使用反射和递归来访问属性。我这样设置地图路径:

map = new Dictionary<string, string>();
map.Add("Name", "Name");
map.Add("Street", "Address.Address");
map.Add("City", "Address.City");
map.Add("State", "Address.State");
map.Add("Zip", "Address.Zip");
map.Add("Activity", "*Hobbies.Hobby");
map.Add("Years", "*Hobbies.Years");

星号表示这是一个列表并且需要一个键。我在处理中添加了密钥,所以我发送的完整路径类似于“*Hiking.Hobbies.Years”,其中徒步旅行是关键。处理方法如下:

private void SetValue(object source, String path, String value)
{
    if (path.Contains('.'))
    {
        //  If this is not the ending Property, continue recursing
        int index = path.IndexOf('.');
        String property = path.Substring(0, index);

        object nextSource;
        if(property.Contains("*"))
        {
            path = path.Substring(index + 1);
            index = path.IndexOf('.');
            String dictionaryName = path.Substring(0, index);
            property = property.Substring(1);

            IDictionary list = source.GetType().GetProperty(dictionaryName).GetValue(source, null) as IDictionary;
            if (!list.Contains(property))
            {
                Type[] arguments = list.GetType().GetGenericArguments();
                list.Add(property, Activator.CreateInstance(arguments[1]));
            }

            nextSource = list[property];                    
        }
        else
            nextSource = source.GetType().GetProperty(property).GetValue(source, null);

        SetValue(nextSource, path.Substring(index + 1), value);
    }
    else
    {
        PropertyInfo pi = source.GetType().GetProperty(path);
        pi.SetValue(source, Convert.ChangeType(value, pi.PropertyType), null);
    }
}

我希望这对某人有所帮助。

【讨论】:

    猜你喜欢
    • 2021-04-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-24
    • 2015-08-27
    相关资源
    最近更新 更多