/
XNodeExtensions.cs
269 lines (244 loc) · 10.1 KB
/
XNodeExtensions.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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
#if NET35_OR_GREATER || TARGETS_NETSTANDARD || TARGETS_NETCOREAPP // PUBLIC_API_CHANGES
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using CodeJam.Strings;
using JetBrains.Annotations;
namespace CodeJam.Xml
{
/// <summary>
/// Extensions for XLinq.
/// </summary>
[PublicAPI]
public static class XNodeExtensions
{
/// <summary>
/// Returns <paramref name="document"/> root, or throw an exception, if root is null.
/// </summary>
/// <param name="document">The document.</param>
/// <returns>Document root</returns>
/// <exception cref="ArgumentNullException"><paramref name="document"/> is null</exception>
/// <exception cref="XmlException">Document has no root.</exception>
[Pure, System.Diagnostics.Contracts.Pure]
public static XElement RequiredRoot(this XDocument document)
{
Code.NotNull(document, nameof(document));
if (document.Root == null)
throw new XmlException("Document root is required");
return document.Root;
}
/// <summary>
/// Returns <paramref name="document"/> root, or throws an exception, if root is null or has another name.
/// </summary>
/// <param name="document">The document.</param>
/// <param name="rootName">Name of the root tag</param>
/// <returns>Document root</returns>
/// <exception cref="ArgumentNullException"><paramref name="document"/> is null</exception>
/// <exception cref="XmlException">Document has no root with specified name.</exception>
[Pure, System.Diagnostics.Contracts.Pure]
public static XElement RequiredRoot(this XDocument document, XName rootName)
{
Code.NotNull(rootName, nameof(rootName));
var root = document.RequiredRoot();
if (root.Name != rootName)
throw new XmlException($"Document root '{rootName}' not found, '{root.Name}' found instead.");
return root;
}
/// <summary>
/// Returns child element with name <paramref name="name"/>, or throws an exception if element does not exists.
/// </summary>
/// <param name="parent">Parent element.</param>
/// <param name="name">Name of the element.</param>
/// <returns>First element with specified name.</returns>
/// <exception cref="ArgumentNullException"><paramref name="parent"/> or <paramref name="name"/> is null.</exception>
/// <exception cref="XmlException">Element with specified name does not exists.</exception>
[Pure, System.Diagnostics.Contracts.Pure]
public static XElement RequiredElement(this XElement parent, XName name)
{
Code.NotNull(parent, nameof(parent));
Code.NotNull(name, nameof(name));
var element = parent.Element(name);
if (element == null)
throw new XmlException($"Element with name '{name}' does not exists.");
return element;
}
/// <summary>
/// Returns child element with one of names in <paramref name="names"/>,
/// or throws an exception if element does not exists.
/// </summary>
/// <param name="parent">Parent element.</param>
/// <param name="names">Possible names of the element.</param>
/// <returns>First element that match one of specified names.</returns>
/// <exception cref="ArgumentNullException">
/// <paramref name="parent"/> or <paramref name="names"/> is null.
/// </exception>
/// <exception cref="XmlException">Element with one of specified names does not exists.</exception>
[Pure, System.Diagnostics.Contracts.Pure]
public static XElement RequiredElement(this XElement parent, params XName[] names)
{
Code.NotNull(parent, nameof(parent));
Code.NotNull(names, nameof(names));
var namesSet = new HashSet<XName>(names);
foreach (var element in parent.Elements())
if (namesSet.Contains(element.Name))
return element;
throw new XmlException($"Element with names {names.Join(", ")} not exists.");
}
/// <summary>
/// Returns attribute with name <paramref name="name"/>, or throws an exception if attribute does not exists.
/// </summary>
/// <param name="element">The <see cref="XElement"/>.</param>
/// <param name="name">Name of the attribute.</param>
/// <returns>Attribute with specified name.</returns>
/// <exception cref="ArgumentNullException">
/// <paramref name="element"/> or <paramref name="name"/> is null.
/// </exception>
/// <exception cref="XmlException">Attribute with specified name not found.</exception>
[Pure, System.Diagnostics.Contracts.Pure]
public static XAttribute RequiredAttribute(this XElement element, XName name)
{
Code.NotNull(element, nameof(element));
Code.NotNull(name, nameof(name));
var attr = element.Attribute(name);
if (attr == null)
throw new XmlException($"Element contains no attribute '{name}'");
return attr;
}
/// <summary>
/// Returns value of optional attribute.
/// </summary>
/// <typeparam name="T">Type of value</typeparam>
/// <param name="element">Element with attribute</param>
/// <param name="attrName">Attribute name.</param>
/// <param name="parser">Value parser</param>
/// <param name="defaultValue">Default value.</param>
/// <returns>Parsed value or <paramref name="defaultValue"/> if attribute not exists.</returns>
/// <exception cref="ArgumentNullException">
/// <paramref name="element"/> or <paramref name="attrName"/> or <paramref name="parser"/> is null.
/// </exception>
[Pure, System.Diagnostics.Contracts.Pure]
public static T AttributeValueOrDefault<T>(
this XElement element,
XName attrName,
[InstantHandle] Func<string, T> parser,
T defaultValue)
{
Code.NotNull(element, nameof(element));
Code.NotNull(attrName, nameof(attrName));
Code.NotNull(parser, nameof(parser));
var attr = element.Attribute(attrName);
return attr != null ? parser(attr.Value) : defaultValue;
}
/// <summary>
/// Returns string value of optional attribute.
/// </summary>
/// <param name="element">Element with attribute</param>
/// <param name="attrName">Attribute name.</param>
/// <param name="defaultValue">Default value.</param>
/// <returns>Parsed value or <paramref name="defaultValue"/> if attribute does not exist.</returns>
/// <exception cref="ArgumentNullException">
/// <paramref name="element"/> or <paramref name="attrName"/> is null.
/// </exception>
[Pure, System.Diagnostics.Contracts.Pure]
public static string AttributeValueOrDefault(
this XElement element,
XName attrName,
string defaultValue)
{
Code.NotNull(element, nameof(element));
Code.NotNull(attrName, nameof(attrName));
return element.Attribute(attrName)?.Value ?? defaultValue;
}
/// <summary>
/// Returns value of optional element.
/// </summary>
/// <typeparam name="T">Type of value</typeparam>
/// <param name="parent">Parent element.</param>
/// <param name="valueSelector">Function to select element value</param>
/// <param name="defaultValue">Default value.</param>
/// <param name="names">Array of possible element names.</param>
/// <returns>Selected element value or <paramref name="defaultValue"/> if element does not exist.</returns>
/// <exception cref="ArgumentNullException">
/// <paramref name="parent"/> or <paramref name="valueSelector"/> or <paramref name="names"/> is null.
/// </exception>
[Pure, System.Diagnostics.Contracts.Pure]
public static T ElementAltValueOrDefault<T>(
this XElement parent,
[InstantHandle] Func<XElement, T> valueSelector,
T defaultValue,
params XName[] names)
{
Code.NotNull(parent, nameof(parent));
Code.NotNull(valueSelector, nameof(valueSelector));
Code.NotNull(names, nameof(names));
var elem = names.Select(parent.Element).FirstOrDefault(e => e != null);
return elem == null ? defaultValue : valueSelector(elem);
}
/// <summary>
/// Returns value of optional element.
/// </summary>
/// <typeparam name="T">Type of value</typeparam>
/// <param name="parent">Parent element.</param>
/// <param name="name">Element name.</param>
/// <param name="valueSelector">Function to select element value</param>
/// <param name="defaultValue">Default value.</param>
/// <returns>Selected element value or <paramref name="defaultValue"/> if element does not exist</returns>
/// <exception cref="ArgumentNullException">
/// <paramref name="parent"/> or <paramref name="valueSelector"/> is null.
/// </exception>
[Pure, System.Diagnostics.Contracts.Pure]
public static T ElementValueOrDefault<T>(
this XElement parent,
XName name,
[InstantHandle] Func<XElement, T> valueSelector,
T defaultValue)
{
Code.NotNull(name, nameof(name));
return ElementAltValueOrDefault(parent, valueSelector, defaultValue, name);
}
/// <summary>
/// Returns value of optional element.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="parent">Parent element.</param>
/// <param name="name">Element name.</param>
/// <param name="valueSelector">Function to parse element value</param>
/// <param name="defaultValue">Default value.</param>
/// <returns>Selected element value or <paramref name="defaultValue"/> if element does not exist</returns>
/// <exception cref="ArgumentNullException">
/// <paramref name="parent"/> or <paramref name="name"/> or <paramref name="valueSelector"/> is null.
/// </exception>
[Pure, System.Diagnostics.Contracts.Pure]
public static T ElementValueOrDefault<T>(
this XElement parent,
XName name,
[InstantHandle] Func<string, T> valueSelector,
T defaultValue)
{
Code.NotNull(name, nameof(name));
return ElementAltValueOrDefault(parent, elem => valueSelector(elem.Value), defaultValue, name);
}
/// <summary>
/// Returns string value of optional element.
/// </summary>
/// <param name="parent">Parent element.</param>
/// <param name="name">Element name.</param>
/// <param name="defaultValue">Default value.</param>
/// <returns>Selected element value or <paramref name="defaultValue"/> if element does not exist</returns>
/// <exception cref="ArgumentNullException">
/// <paramref name="parent"/> or <paramref name="name"/> is null.
/// </exception>
[Pure, System.Diagnostics.Contracts.Pure]
public static string ElementValueOrDefault(
this XElement parent,
XName name,
string defaultValue)
{
Code.NotNull(name, nameof(name));
return ElementAltValueOrDefault(parent, e => e.Value, defaultValue, name);
}
}
}
#endif