Cannot write the correct value on Json file - c#

I'm trying to write a function that wirte on my Json file the value I enter on PostMan
I have my Json file as following:
[
{
"targets":[
"192.168.1.101:9182",
"192.168.1.103:9100",
"192.168.1.105:9182"
]
}
]
M tried to for example a target with the following query:
{
"targets": [
"192.168.1.117:9100"
]
}
Here is my class model:
public class targetResponse
{
public IList<string> targets { get; set; }
}
using postman as shown in the picture:
Everything works fine in the part of prometheus (gives me no error).
Here is the function that I use:
[HttpPost]
[ProducesResponseType(typeof(targetResponse), StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<ActionResult<targetResponse>> PostNewInstanceToConfig([FromBody] targetResponse _target)
{
IList<targetResponse> myList = new List<targetResponse>();
var jsonString = System.IO.File
.ReadAllText(_filePath);
myList = JsonConvert.DeserializeObject<List<targetResponse>>(jsonString);
myList.FirstOrDefault().targets.Add(_target.ToString());
StreamWriter myWriter = new StreamWriter(_filePath);
JsonSerializer mySerializer = new JsonSerializer();
mySerializer.Serialize(myWriter, myList);
myWriter.Close();
return null;
}
It opens the file and write following thing, I dont understand why it doesnt why:
[
{
"targets": [
"192.168.1.101:9182",
"192.168.1.103:9100",
"192.168.1.105:9182",
"ApplicationTestCRUD_JSON.targetResponse",
"ApplicationTestCRUD_JSON.targetResponse"
]
}
]
The "ApplicationTestCRUD_JSON" is the name of my project.
Any help please?
EDIT:
I added a foreach loop and removed .toString() method, my function now looks like this:
[HttpPost]
[ProducesResponseType(typeof(targetResponse), StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<ActionResult<targetResponse>> PostNewInstanceToConfig([FromBody] targetResponse _target)
{
IList<targetResponse> myList = new List<targetResponse>();
var jsonString = System.IO.File
.ReadAllText(_filePath);
myList = JsonConvert.DeserializeObject<List<targetResponse>>(jsonString);
foreach(var x in _target.targets)
{
myList.FirstOrDefault().targets.Add(x);
}
StreamWriter myWriter = new StreamWriter(_filePath);
JsonSerializer mySerializer = new JsonSerializer();
mySerializer.Serialize(myWriter, myList);
myWriter.Close();
return null;
}

You are doing _target.ToString() and since _target is of type targetResponse you get the default ToString text back, which is the full name of the object type.
You probably want to access a property of _target instead.

Related

How do I return correctly formatted name:value pairs from mongodb, using the C# mongo driver [duplicate]

I am working with the MongoDB C# driver. I have a BsonDocument with some data which includes some MongoDB-specific types (like ObjectIDs and ISODates). I want to convert this to a valid general-purpose JSON string. In other words, I can't have something like _id: ObjectId(...) or date: ISODate(...) but would prefer _id: "..." and date: "...". Basically, I want to convert these special types that only MongoDB recognizes to regular strings so they can be parsed more easily. The problem is that a built-in function like .ToJson() (which another StackOverflow answer suggests) doesn't really convert the document to valid JSON at all because it maintains these special types. My document also contains many levels of arrays and sub-documents, so a simple for loop will not suffice. What's the best way to convert a BsonDocument that avoids this problem? I would prefer something built-in rather than manually recursing through the document to fix all the issues.
MongoDB.Bson (2.5+) has support to map between BsonValues and .Net objects.
BsonTypeMapper Class
To map a BsonValue (or BsonDocument) to .Net object use
var dotNetObj = BsonTypeMapper.MapToDotNetValue(bsonDoc);
You can then use your choice of serialization library. For example,
JsonConvert.SerializeObject(dotNetObj);
If you have a List of BsonDocument
var dotNetObjList = bsonDocList.ConvertAll(BsonTypeMapper.MapToDotNetValue);
I've ran into the same thing, you can get valid JSON via:
var jsonWriterSettings = new JsonWriterSettings { OutputMode = JsonOutputMode.Strict };
JObject json = JObject.Parse(postBsonDoc.ToJson<MongoDB.Bson.BsonDocument>(jsonWriterSettings));
However it will return something like:
{"_id":{"$oid":"559843798f9e1d0fe895c831"}, "DatePosted":{"$date":1436107641138}}
I'm still trying to find a way to flatten that.
In my opinion the best option is to use Newtonsoft.Json.Bson.BsonReader.
Here a complete example:
public string ToJson(BsonDocument bson)
{
using (var stream = new MemoryStream())
{
using (var writer = new BsonBinaryWriter(stream))
{
BsonSerializer.Serialize(writer, typeof(BsonDocument), bson);
}
stream.Seek(0, SeekOrigin.Begin);
using (var reader = new Newtonsoft.Json.Bson.BsonReader(stream))
{
var sb = new StringBuilder();
var sw = new StringWriter(sb);
using (var jWriter = new JsonTextWriter(sw))
{
jWriter.DateTimeZoneHandling = DateTimeZoneHandling.Utc;
jWriter.WriteToken(reader);
}
return sb.ToString();
}
}
}
I think that this should handle all cases correctly (dates, ids, ...).
Most of the Time for this I use, Json.NET
JsonConvert.SerializeObject(obj);
Most of the time that does the trick. If need be you can set some JsonSerializerSettings
Through experimentation I discovered that there is an option that makes this method output proper JSON:
BsonDocument myBsonDocument = ... //code that loads a BSON document
myBsonDocument.ToJson(new JsonWriterSettings { OutputMode = JsonOutputMode.RelaxedExtendedJson})
Result:
{ "_id" : { "$oid" : "5fb7a33e73152101d6610e9d" }, "moreProperties" : "moreValues" }
Here is the way i did it, to skip mongodb _id entry.
var collection = _database.GetCollection<BsonDocument>("test");
var result = await collection.Find(new BsonDocument())
.Project(Builders<BsonDocument>.Projection.Exclude("_id"))
.ToListAsync();
var obj = result.ToJson();
what about
String json = result.toJson(JsonWriterSettings.builder().objectIdConverter(new Converter<ObjectId>() {
#Override
public void convert(ObjectId value, StrictJsonWriter writer) {
writer.writeString(value.toHexString());
}
}).build());
If the contents of the BSON document is saved as, below
{
"Date" : "2019-04-05T07:07:31.979Z",
"BSONCONTENT" : {
"_t" : "MongoDB.Bson.BsonDocument, MongoDB.Bson",
"_v" : {
"A" : "XXXX",
"B" : 234
}
}
}
then it works with generic class.
private static T ProcessBsonConversion<T>(BsonDocument data)
{
var content = data.GetElement("_v");
var jsonDataContent= content.Value.AsBsonValue.ToJson();
return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(jsonDataContent);
}
My problem had to do with how DotNet Core WebAPI serializes an object to json. If you return a string from a method that is formatted as json, WEBAPI will serialize it to json again. This is only needed if you are working with a generic BsonDocument to save to MongoDb.
[HttpGet()]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<string>> GetAsync()
{
return Ok(ret.ToJson());
}
Fix
[HttpGet()]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<object>> GetAsync()
{
var doc = await _collection.Find(...).FirstOrDefaultAsync();
return Ok(JObject.Parse(doc.ToJson()));
}
Since Davide Icardi answer is deprecated so:
Install Newtonsoft.Json.Bson package
Replace BsonReader with BsonDataReader.
Your extension method should be like this:
using MongoDB.Bson;
using MongoDB.Bson.IO;
using MongoDB.Bson.Serialization;
using Newtonsoft.Json;
using Newtonsoft.Json.Bson;
using System.IO;
using System.Text;
namespace YourNamespaceGoesHere
{
public static class BsonHelpers
{
public static string ToNormalJson(BsonDocument bson)
{
using (var stream = new MemoryStream())
{
using (var writer = new BsonBinaryWriter(stream))
{
BsonSerializer.Serialize(writer, typeof(BsonDocument), bson);
}
stream.Seek(0, SeekOrigin.Begin);
using (var reader = new BsonDataReader(stream))
{
var sb = new StringBuilder();
var sw = new StringWriter(sb);
using (var jWriter = new JsonTextWriter(sw))
{
jWriter.DateTimeZoneHandling = DateTimeZoneHandling.Utc;
jWriter.WriteToken(reader);
}
return sb.ToString();
}
}
}
}
}
This should generate the expected normal valid JSON string you're looking for :)
If you need to use this ASP.NET Core for when you may be returning a model that has BsonDocument to be able to add dynamic data. You can use this JsonConverter implementation based on MarkKGreenway's answer!
public class BsonDocumentJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(BsonDocument);
}
public override bool CanRead
{
get
{
return false;
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
//string json = (value as BsonDocument).ToJson(); //!NB: this returns BSON not JSON. Why on earth is it called ToJson!?
string json = JsonConvert.SerializeObject(value);
writer.WriteRawValue(json);
}
}
Then in your Startup.cs just add the following.
services.AddMvc()
.AddJsonOptions(options => options.SerializerSettings.Converters.Add(new BsonDocumentJsonConverter()));

How can i make a flat object by removing keys in C#?

I am really not sure if this could be achievable, How can i remove some nested keys in an Object and make the object very flat. I have a dynamic object as follows,
EventData": { "ChangeSet": { "Change": {
"changes": [
] } } }
and i want to change the above to
EventData": { [] }
is this can be achieved in C#?
Use the NewtonSoft.JSon package.. Following code does the trick. I made it a string array because I do not know what you need but you can change this to your liking.
const string complex = "{\"EventData\": { \"ChangeSet\": { \"Change\": { \"changes\" : [ ]}}}}";
Call to method:
string simple = returnSimpleObject(complex);
public class SerializeData
{
public string[] EventData { get; set; }
}
private static string returnSimpleObject(string Json)
{
JObject jobject = JObject.Parse(Json);
JToken tEventData = jobject.SelectToken("EventData");
SerializeData myEvent = tEventData.ToObject<SerializeData>();
JToken tchanges = jobject.SelectToken("EventData.ChangeSet.Change.changes");
myEvent.EventData = tchanges.ToObject<string[]>();
JsonSerializer serializer = new JsonSerializer();
StringWriter strWrite = new StringWriter();
JsonWriter myWriter = new JsonTextWriter(strWrite);
serializer.Serialize(myWriter, myEvent);
return strWrite.ToString();
}

Parse JSON elegantly

I have a web api controller in .NET Core 2.1, which receives
JToken jsonBody
The json has the following structure
{
"id": "xxx",
"payload": {
"TelephoneNumber": "1111",
"Name": "Hans"
}
}
and more fields, but it's irrelevant.
I want to retrieve the Number and Name elegantly. Currently, I do the following, which I'm sure could be done in a nicer way:
var payload = JObject.Parse(jsonBody.SelectToken("Payload").ToString());
telephoneNumber = new TelephoneNumber(payload.SelectToken("TelephoneNumber").ToString());
I've tried just doing
jsonBody.SelectToken("Payload.TelephoneNumber")
but that doesn't work. I think that it's because somehow the jsonBody, that the controller receives, has only parsed the top nodes as json, hence it could be that it regards the value of
jsonBody.SelectToken("Payload")
as a string.
As per official documentation - you can do something like this:
var phone = jsonBody["payload"]["TelephoneNumber"].ToString();
var name = jsonBody["payload"]["Name"].ToString();
See a live demo on rextester.
This is at least a little bit more elegant:
var jsonBody = JObject.Parse(#"{
'id': 'xxx',
'payload': {
'TelephoneNumber': '1111',
'Name': 'Hans'
}
}");
var phone = jsonBody["payload"]["TelephoneNumber"].Value<string>();
var name = jsonBody["payload"]["Name"].Value<string>();
If you don't want to deserialize your full json, you can create a class with the properties you need
public class Payload
{
public string TelephoneNumber { get; set; }
public string Name { get; set; }
}
And then use JsonTextReader to deserialize the string:
private static Payload DeserializePayload(JToken token)
{
var serializer = new JsonSerializer();
using (JsonTextReader reader = new JsonTextReader(new StringReader(token.ToString())))
{
reader.CloseInput = true;
while (reader.Read())
{
if (reader.TokenType == JsonToken.StartObject && reader.Path.Equals("payload"))
{
var payload = serializer.Deserialize<Payload>(reader);
return payload;
}
}
}
// not found - return null? throw exception?
return null;
}
Testing the code:
var token = JToken.Parse(#"{
""id"": ""xxx"",
""payload"": {
""TelephoneNumber"": ""1111"",
""Name"": ""Hans""
}
}");
Payload payload = DeserializePayload(token);
Console.WriteLine($"Name: {payload.Name}, Phone number: {payload.TelephoneNumber}");

Encode JSON with custom property names

I want to make a POST request to the Hubspot API (https://developers.hubspot.com/docs/methods/contacts/create_or_update) and I need the JSON to match this format:
{ "properties": [ { "property": "firstname", "value": "HubSpot" } ] }
Instead im getting this:
{ "properties": [ { "Key": "email", "Value": "alphatest#baconcompany.com" }, { "Key": "firstname", "Value": "testfirstname" } ] }
Instead of "property" and "value" , my code generates "Key" and "Value" , how can I change my JSON to match the right format?
Here is how I generate that dictionary:
public class HubspotContact
{
public Dictionary<string, string> properties { get; set; }
}
private static readonly HttpClient client = new HttpClient();
class DictionaryAsArrayResolver : DefaultContractResolver
{
protected override JsonContract CreateContract(Type objectType)
{
if (objectType.GetInterfaces().Any(i => i == typeof(IDictionary) ||
(i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IDictionary<,>))))
{
return base.CreateArrayContract(objectType);
}
return base.CreateContract(objectType);
}
}
And this is how I generate the JSON:
HubspotContact foo = new HubspotContact();
foo.properties = new Dictionary<string, string>();
foo.properties.Add("email", "alphatest#baconcompany.com");
foo.properties.Add("firstname", "firstname");
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.Formatting = Formatting.Indented;
settings.ContractResolver = new DictionaryAsArrayResolver();
string json = JsonConvert.SerializeObject(foo, settings);
Finally this is how I send my request:
var httpWebRequest =(HttpWebRequest)WebRequest.Create("https://api.hubapi.com/contacts/v1/contact/createOrUpdate/email/alphatest#baconcompany.com/?hapikey=myapikey");
httpWebRequest.ContentType = "application/json";
httpWebRequest.Method = "POST";
using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream())){
streamWriter.Write(json);
streamWriter.Flush();
streamWriter.Close();
}
var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
var result = streamReader.ReadToEnd();
Console.WriteLine(result.ToString());
}
Right now with the request as is I get this error:
System.Net.WebException: 'The remote server returned an error: (400) Bad Request.'
Change HubspotContact to this:
class PropertyValue
{
public string Property { get;set;}
public string Value { get;set;}
}
class HubspotContact
{
public List<PropertyValue> Properties {get;set;}
}
It should serialize to a proper format and it does not need custom serializer.
C#'s Json serializer does not support custom naming of properties coming from any generic data structure...
Try replacing the Dictionary with a custom class containing the properties "property" and "value" instead.

Deserialization of JSON object with sub field as string using DataContractJsonSerializer in C#

Here is my JSON:
{
"Name": "Eli",
"Age": 4,
"Children": {
"Moshe": {
"Age": 6,
"Characteristics": "Nice;Good;"
},
"Yossi": {
"Age": 3,
"Characteristics": "Hero;Brave"
}
}
}
Here is my JSON deserialization function:
public static object FromJSON<T>(string json)
{
using (MemoryStream stream = new MemoryStream(Encoding.Unicode.GetBytes(json)))
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
return serializer.ReadObject(stream);
}
}
I'm trying to serialize it to Person object:
[DataContract]
public class Person
{
[DataMember]
public int Age;
[DataMember]
public string Name;
[DataMember]
public string Children;
}
As you can see, I do not want to get Children into dictionary but to get it as is - JSON string.
Currently I'm doing it like this:
Person p = (Person)Extensions.FromJSON<Person>(output);
And I'm getting exception saying:
There was an error deserializing the object of type JSONSerialization.Person. End element 'item' from namespace 'item' expected. Found element 'a:item' from namespace 'item'.
Just to clarify: I do not want the children sub field to be parsed.
If you use the JavaScriptSerializer then you will be able to serialize the Children key into a dynamic field and read it in JSON format if required.
In the Person object try and use the dynamic type instead of string for the Children property.
public static T FromJavaScriptSerializer<T>(string json)
{
var serializer = new JavaScriptSerializer();
return serializer.Deserialize<T>(json);
}
public static string ToStringOutput(object dynamicField)
{
var serializer = new JavaScriptSerializer();
return serializer.Serialize(dynamicField);
}
You can use the methods as follows
var person = FromJavaScriptSerializer<Person>(output);
Console.WriteLine(ToStringOutput(person.Children));
Following will appear in console
{"Moshe":{"Age":6,"Characteristics":"Nice;Good;"},"Yossi":{"Age":3,"Characteristics":"Hero;Brave"}}
Use Regex to convert Children to string (add quotes, replace new lines with \n) before the deserialization https://dotnetfiddle.net/GWs69U
string result = Regex.Replace(json, #"(\""Children\""\:(.|\n)*?)(\{(.|\n)*\{(.|\n)*?\}(.|\n)*?\})", (m) =>
{
return (m.Groups[1] + "\"" + Regex.Replace(m.Groups[3].Value.Replace("\n", " "), #"\s+", " ") + "\"");
});

Categories