/
BsonAnonymousTypeParser.cs
131 lines (99 loc) · 4.2 KB
/
BsonAnonymousTypeParser.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text.RegularExpressions;
namespace CSMongo.Bson {
//<summary>
//Class designed to use an AnonymousType as a template
//for a return value using a BsonObject for a data source
//Really shouldn't be used for anything else since it
//only works with the BsonObjects
internal class BsonAnonymousTypeParser {
#region Constructors
/// <summary>
/// Creates a new parser for the provided information
/// </summary>
public BsonAnonymousTypeParser(BsonObject data) {
this._Data = data;
}
#endregion
#region Fields
//houses the information to use
private BsonObject _Data;
#endregion
#region Parsing
//handles reading a node of anonymous type and
//returns the values for the type - This section
//is heavily commented to try and explain what it
//doing
public object ReadType(string parent, object section) {
//create a list of values for the constructor
List<object> values = new List<object>();
//and also get this type so we know how to create a
//new instance using the constructor
Type type = section.GetType();
//start checking each of the properties
foreach (PropertyInfo property in type.GetProperties()) {
//get the path to the value in the document
//but only if this isn't the first set
string path = string.IsNullOrEmpty(parent)
? property.Name
: string.Concat(parent, ".", property.Name);
//get the value to use
object value = property.GetValue(section, null);
//if this is anonymous type, parse it
if (BsonAnonymousTypeParser.IsAnonymousType(value)) {
value = this.ReadType(path, value);
}
//if it is not, check to see if there is an existing value
else {
//try and get the value to use
value = this._Data.Get(path, value);
}
//add this to the list of values
values.Add(value);
}
//now that our object is created, try and create the instance
//but if it fails just give up and return the template value
try {
return Activator.CreateInstance(type, values.ToArray());
}
catch {
return section;
}
}
#endregion
#region Static Methods
/// <summary>
/// Attempts to distingusing anonymous types from
/// other object types
/// </summary>
public static bool IsAnonymousType(object value) {
//make sure this has a value
if (value == null) { return false; }
//check if this is anonymous type using the name and
//some values that hint it might be anonymous - This
//could probably be better though if a better way of
//checking for anonymous types becomes available
Type type = value.GetType();
return Regex.IsMatch(type.FullName, "^(<>f__AnonymousType|VB\\$AnonymousType)") &&
type.IsSealed &&
type.IsGenericType &&
type.BaseType.Equals(typeof(object));
}
/// <summary>
/// Attempts to assign the values of a BsonObject into new Anonymous Type values
/// </summary>
public static T PopulateAnonymousType<T>(BsonObject data, T template) {
return BsonAnonymousTypeParser.PopulateAnonymousType<T>(data, string.Empty, template);
}
/// <summary>
/// Attempts to assign the values of a BsonObject into new Anonymous Type values
/// </summary>
public static T PopulateAnonymousType<T>(BsonObject data, string parent, T template) {
BsonAnonymousTypeParser parser = new BsonAnonymousTypeParser(data);
return (T)parser.ReadType(parent, template);
}
#endregion
}
}