recorrer - parse string to json c#
¿Cómo convertir NameValueCollection a cadena JSON? (4)
Para completar, y porque la pregunta continúa siendo formulada (por ejemplo, here ), siempre que esté utilizando Json.NET o DataContractJsonSerializer
(pero no JavaScriptSerializer
) , puede usar el patrón del adaptador y envolver el NameValueCollection
en un IDictionary<string, string[]>
adaptador, y serialice eso usando cualquier serializador que admita completamente serializar diccionarios arbitrarios.
Una vez que dicho adaptador es el siguiente:
public class NameValueCollectionDictionaryAdapter<TNameValueCollection> : IDictionary<string, string[]>
where TNameValueCollection : NameValueCollection, new()
{
readonly TNameValueCollection collection;
public NameValueCollectionDictionaryAdapter() : this(new TNameValueCollection()) { }
public NameValueCollectionDictionaryAdapter(TNameValueCollection collection)
{
this.collection = collection;
}
// Method instead of a property to guarantee that nobody tries to serialize it.
public TNameValueCollection GetCollection() { return collection; }
#region IDictionary<string,string[]> Members
public void Add(string key, string[] value)
{
if (collection.GetValues(key) != null)
throw new ArgumentException("Duplicate key " + key);
if (value == null)
collection.Add(key, null);
else
foreach (var str in value)
collection.Add(key, str);
}
public bool ContainsKey(string key) { return collection.GetValues(key) != null; }
public ICollection<string> Keys { get { return collection.AllKeys; } }
public bool Remove(string key)
{
bool found = ContainsKey(key);
if (found)
collection.Remove(key);
return found;
}
public bool TryGetValue(string key, out string[] value)
{
return (value = collection.GetValues(key)) != null;
}
public ICollection<string[]> Values
{
get
{
return new ReadOnlyCollectionAdapter<KeyValuePair<string, string[]>, string[]>(this, p => p.Value);
}
}
public string[] this[string key]
{
get
{
var value = collection.GetValues(key);
if (value == null)
throw new KeyNotFoundException(key);
return value;
}
set
{
Remove(key);
Add(key, value);
}
}
#endregion
#region ICollection<KeyValuePair<string,string[]>> Members
public void Add(KeyValuePair<string, string[]> item) { Add(item.Key, item.Value); }
public void Clear() { collection.Clear(); }
public bool Contains(KeyValuePair<string, string[]> item)
{
string[] value;
if (!TryGetValue(item.Key, out value))
return false;
return EqualityComparer<string[]>.Default.Equals(item.Value, value); // Consistent with Dictionary<TKey, TValue>
}
public void CopyTo(KeyValuePair<string, string[]>[] array, int arrayIndex)
{
foreach (var item in this)
array[arrayIndex++] = item;
}
public int Count { get { return collection.Count; } }
public bool IsReadOnly { get { return false; } }
public bool Remove(KeyValuePair<string, string[]> item)
{
if (Contains(item))
return Remove(item.Key);
return false;
}
#endregion
#region IEnumerable<KeyValuePair<string,string[]>> Members
public IEnumerator<KeyValuePair<string, string[]>> GetEnumerator()
{
foreach (string key in collection)
yield return new KeyValuePair<string, string[]>(key, collection.GetValues(key));
}
#endregion
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); }
#endregion
}
public static class NameValueCollectionExtensions
{
public static NameValueCollectionDictionaryAdapter<TNameValueCollection> ToDictionaryAdapter<TNameValueCollection>(this TNameValueCollection collection)
where TNameValueCollection : NameValueCollection, new()
{
if (collection == null)
throw new ArgumentNullException();
return new NameValueCollectionDictionaryAdapter<TNameValueCollection>(collection);
}
}
public class ReadOnlyCollectionAdapter<TIn, TOut> : CollectionAdapterBase<TIn, TOut, ICollection<TIn>>
{
public ReadOnlyCollectionAdapter(ICollection<TIn> collection, Func<TIn, TOut> toOuter)
: base(() => collection, toOuter)
{
}
public override void Add(TOut item) { throw new NotImplementedException(); }
public override void Clear() { throw new NotImplementedException(); }
public override bool IsReadOnly { get { return true; } }
public override bool Remove(TOut item) { throw new NotImplementedException(); }
}
public abstract class CollectionAdapterBase<TIn, TOut, TCollection> : ICollection<TOut>
where TCollection : ICollection<TIn>
{
readonly Func<TCollection> getCollection;
readonly Func<TIn, TOut> toOuter;
public CollectionAdapterBase(Func<TCollection> getCollection, Func<TIn, TOut> toOuter)
{
if (getCollection == null || toOuter == null)
throw new ArgumentNullException();
this.getCollection = getCollection;
this.toOuter = toOuter;
}
protected TCollection Collection { get { return getCollection(); } }
protected TOut ToOuter(TIn inner) { return toOuter(inner); }
#region ICollection<TOut> Members
public abstract void Add(TOut item);
public abstract void Clear();
public virtual bool Contains(TOut item)
{
var comparer = EqualityComparer<TOut>.Default;
foreach (var member in Collection)
if (comparer.Equals(item, ToOuter(member)))
return true;
return false;
}
public void CopyTo(TOut[] array, int arrayIndex)
{
foreach (var item in this)
array[arrayIndex++] = item;
}
public int Count { get { return Collection.Count; } }
public abstract bool IsReadOnly { get; }
public abstract bool Remove(TOut item);
#endregion
#region IEnumerable<TOut> Members
public IEnumerator<TOut> GetEnumerator()
{
foreach (var item in Collection)
yield return ToOuter(item);
}
#endregion
#region IEnumerable Members
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
#endregion
}
Luego, se puede construir una adaptación para un determinado NameValueCollection Data
simplemente haciendo:
var adapter = Data.ToDictionaryAdapter();
Notas:
El uso del adaptador puede ser más eficaz que simplemente crear un diccionario copiado, y debería funcionar bien con cualquier serializador que sea totalmente compatible con la serialización de diccionarios.
El adaptador también podría ser útil para usar una
NameValueCollection
con cualquier otro código que espere unIDictionary
de algún tipo; esta es la ventaja fundamental del patrón de adaptador.Dicho esto,
JavaScriptSerializer
no se puede usar con el adaptador porque este serializador no puede serializar un tipo arbitrario implementandoIDictionary<TKey, TValue>
que no se hereda también delDictionary<TKey, TValue>
. Para obtener más información, consulte Serialización de diccionarios con JavaScriptSerializer .Al usar
DataContractJsonSerializer
, unNameValueCollection
puede reemplazarse con un adaptador en el gráfico de serialización utilizando el mecanismo de sustitución de contrato de datos .Cuando se utiliza Json.NET, se puede reemplazar un
NameValueCollection
con un adaptador usando unJsonConverter
personalizado como el siguiente:public class NameValueJsonConverter<TNameValueCollection> : JsonConverter where TNameValueCollection : NameValueCollection, new() { public override bool CanConvert(Type objectType) { return typeof(TNameValueCollection).IsAssignableFrom(objectType); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (reader.SkipComments().TokenType == JsonToken.Null) return null; // Reuse the existing NameValueCollection if present var collection = (TNameValueCollection)existingValue ?? new TNameValueCollection(); var dictionaryWrapper = collection.ToDictionaryAdapter(); serializer.Populate(reader, dictionaryWrapper); return collection; } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { var collection = (TNameValueCollection)value; var dictionaryWrapper = new NameValueCollectionDictionaryAdapter<TNameValueCollection>(collection); serializer.Serialize(writer, dictionaryWrapper); } } public static partial class JsonExtensions { public static JsonReader SkipComments(this JsonReader reader) { while (reader.TokenType == JsonToken.Comment && reader.Read()) ; return reader; } }
Que se podría utilizar, por ejemplo, de la siguiente manera:
string json = JsonConvert.SerializeObject(Data, Formatting.Indented, new NameValueJsonConverter<NameValueCollection>());
NameValueCollection
admite todos los siguientes- Un valor
null
para una clave dada; - Múltiples valores para una clave dada (en cuyo caso
NameValueCollection.Item[String]
devuelve una lista de valores separados por comas); - Un solo valor que contiene una coma incrustada (que no se puede distinguir del caso de múltiples valores cuando se utiliza
NameValueCollection.Item[String]
).
Por lo tanto, el adaptador debe implementarIDictionary<string, string[]>
lugar deIDictionary<string, string>
y también tener cuidado de manejar una matriz de valornull
.- Un valor
Fiddle de muestra (incluidas algunas pruebas unitarias básicas) aquí: https://dotnetfiddle.net/gVPSi7
Lo intenté:
NameValueCollection Data = new NameValueCollection();
Data.Add("foo","baa");
string json = new JavaScriptSerializer().Serialize(Data);
devuelve: ["foo"]
Esperé {"foo" : "baa"}
¿Cómo hago esto?
Si su diccionario no pretende contener muchas entradas, puede usar la clase: System.Collections.Specialized.ListDictionary
Una forma de serializar NameValueCollection es convirtiéndolo primero en Diccionario y luego serializar el Diccionario. Para convertir al diccionario:
thenvc.AllKeys.ToDictionary(k => k, k => thenvc[k]);
Si necesita hacer la conversión con frecuencia, también puede crear un método de extensión para NameValueCollection:
public static class NVCExtender
{
public static IDictionary<string, string> ToDictionary(
this NameValueCollection source)
{
return source.AllKeys.ToDictionary(k => k, k => source[k]);
}
}
para que puedas hacer la conversión en una línea como esta:
NameValueCollection Data = new NameValueCollection();
Data.Add("Foo", "baa");
var dict = Data.ToDictionary();
Entonces puedes serializar el diccionario:
var json = new JavaScriptSerializer().Serialize(dict);
// you get {"Foo":"baa"}
Pero NameValueCollection puede tener varios valores para una clave, por ejemplo:
NameValueCollection Data = new NameValueCollection();
Data.Add("Foo", "baa");
Data.Add("Foo", "again?");
Si serializas esto obtendrás {"Foo":"baa,again?"}
.
Puede modificar el convertidor para producir IDictionary<string, string[]>
lugar:
public static IDictionary<string, string[]> ToDictionary(
this NameValueCollection source)
{
return source.AllKeys.ToDictionary(k => k, k => source.GetValues(k));
}
Así que puedes obtener un valor serializado como este: {"Foo":["baa","again?"]}
.
NameValueCollection
no es un IDictionary, por lo que JavaScriptSerializer
no puede serializarlo como espera directamente. Primero deberá convertirlo en un diccionario y luego serializarlo.
Actualización : siguiendo las preguntas relacionadas con múltiples valores por clave, la llamada a nvc[key]
simplemente los devolverá separados por una coma, lo que puede estar bien. De lo contrario, siempre se puede llamar a GetValues
y decidir qué hacer con los valores de manera adecuada. Se actualizó el código a continuación para mostrar una manera posible.
public class _7003740
{
static Dictionary<string, object> NvcToDictionary(NameValueCollection nvc, bool handleMultipleValuesPerKey)
{
var result = new Dictionary<string, object>();
foreach (string key in nvc.Keys)
{
if (handleMultipleValuesPerKey)
{
string[] values = nvc.GetValues(key);
if (values.Length == 1)
{
result.Add(key, values[0]);
}
else
{
result.Add(key, values);
}
}
else
{
result.Add(key, nvc[key]);
}
}
return result;
}
public static void Test()
{
NameValueCollection nvc = new NameValueCollection();
nvc.Add("foo", "bar");
nvc.Add("multiple", "first");
nvc.Add("multiple", "second");
foreach (var handleMultipleValuesPerKey in new bool[] { false, true })
{
if (handleMultipleValuesPerKey)
{
Console.WriteLine("Using special handling for multiple values per key");
}
var dict = NvcToDictionary(nvc, handleMultipleValuesPerKey);
string json = new JavaScriptSerializer().Serialize(dict);
Console.WriteLine(json);
Console.WriteLine();
}
}
}