I have a problem with reading and deserializing the HTTP response from my simple web API that supposes to return IEnumerable of ContactDto object. In contentReader.Read() line I'm getting an error as:
"Data at the root level is invalid. Line 1, position 1."
Controller get code:
[HttpGet]
// GET api/<controller>
public IEnumerable<ContactDto> Get()
{
return new List<ContactDto>()
{
new ContactDto()
{
DateOfBirth = DateTime.Today,
FirstName = "tmp",
LastName = "tmp"
}
};
}
Model code:
public class ContactDto
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime DateOfBirth { get; set; }
}
Reading and parsing with HttpClient:
public IEnumerable<ContactDto> Read()
{
var serializer = new XmlSerializer(typeof(ContactDto));
var client =new HttpClient();
var stream = client.GetStreamAsync(_feedUrl).Result;
using (var streamReader = new StreamReader(stream))
{
using (var contentReader = new XmlTextReader(streamReader))
{
while (contentReader.Read())
{
var innerEventXml = contentReader.ReadAsString();
using (var stringReader = new StringReader(innerEventXml))
{
yield return (ContactDto) serializer.Deserialize(stringReader);
}
}
}
}
}
Returning a list of objects in your Get action results in a JSON result when calling it, not a XML result.
You can deserialize it using Newtonsoft.Json for example:
string result = client.GetStringAsync().Result;
return JsonConvert.Deserialize<List<ContactDto>>(result);
Althought I would suggest you to use await for the async methods.
Related
I am having trouble deserializing a json api.
This is my api endpoint: https://www.googleapis.com/books/v1/volumes?q=harry+potter
The problem I have is: The JSON value could not be converted to System.Collections.Generic.IEnumerable at LineNumber: 0 | BytePositionInLine:1
failing at: Books = await JsonSerializer.DeserializeAsync<IEnumerable<Book>>(responseStream);
I think the reason is it is starting the parsing from the root, where it is receiving an object.
Is there a way to skip the "kind" and "totalItems" node and start directly from the "items" node?
public async Task<IActionResult> Index()
{
var message = new HttpRequestMessage();
message.Method = HttpMethod.Get;
message.RequestUri = new Uri(URL);
message.Headers.Add("Accept", "application/json");
var client = _clientFactory.CreateClient();
var response = await client.SendAsync(message);
if (response.IsSuccessStatusCode)
{
using var responseStream = await response.Content.ReadAsStreamAsync();
Books = await JsonSerializer.DeserializeAsync<IEnumerable<Book>>(responseStream);
}
else
{
GetBooksError = true;
Books = Array.Empty<Book>();
}
return View(Books);
}
Model Class:
public class Book
{
[Display(Name = "ID")]
public string id { get; set; }
[Display(Name = "Title")]
public string title { get; set; }
[Display(Name = "Authors")]
public string[] authors { get; set; }
[Display(Name = "Publisher")]
public string publisher { get; set; }
[Display(Name = "Published Date")]
public string publishedDate { get; set; }
[Display(Name = "Description")]
public string description { get; set; }
[Display(Name = "ISBN 10")]
public string ISBN_10 { get; set; }
[Display(Name = "Image")]
public string smallThumbnail { get; set; }
}
I found a way to do this using JsonDocument. Its not very elegant because you are basically parsing the json twice but it should work.
var responseStream = await response.Content.ReadAsStreamAsync();
// Parse the result of the query to a JsonDocument
var document = JsonDocument.Parse(responseStream);
// Access the "items" collection in the JsonDocument
var booksElement = document.RootElement.GetProperty("items");
// Get the raw Json text of the collection and parse it to IEnumerable<Book>
// The JsonSerializerOptions make sure to ignore case sensitivity
Books = JsonSerializer.Deserialize<IEnumerable<Book>>(booksElement.GetRawText(), new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
I used the answer to this question to create this solution.
I am developing an api, which has to return csv file on some endpoint. Here's my controller responsible for csv generation:
[ApiController]
[Route("api/[controller]")]
[Authorize]
public sealed class ReportController : BaseController
{
public ReportController(ICommandBus commandBus,
IQueryBus queryBus)
: base(commandBus, queryBus)
{
}
[HttpGet]
public async Task<IActionResult> GetReportAsync([FromQuery] GenerateReportRequest request)
{
try
{
var report = await QueryBus
.SendAsync<GenerateReportQuery, Report>(new GenerateReportQuery
{
Filters = request.Filters,
ResponseFileFormat = request.ResponseFileFormat,
WithPodOnly = request.WithPodOnly
});
return File(report.Content,
report.Type,
report.Name);
}
catch (Exception e)
{
// ToDo: Handle exception in proper way
return StatusCode(StatusCodes.Status500InternalServerError,
e.Message);
}
}
}
When the request comes to my api, certain handler is invoked, and the csv generation starts in CsvGenerationStrategy class, which is attached below:
public class CsvGenerationStrategy : IReportGenerationStrategy
{
public async Task<Report> GenerateReportAsync(ICollection<ShipmentEntity> shipmentEntities)
{
var shipment = shipmentEntities
.Select(s => (Shipment) s)
.ToList();
await using var memoryStream = new MemoryStream();
await using var streamWriter = new StreamWriter(memoryStream);
await using var csvWriter = new CsvWriter(streamWriter, CultureInfo.InvariantCulture);
csvWriter.Configuration.Delimiter = ";";
await csvWriter.WriteRecordsAsync(shipment);
var content = memoryStream.ToArray();
var report = new Report
{
Content = content,
Type = ReportConstants.CsvFileType,
Name = ReportConstants.CsvReportFileName
};
return report;
}
private class Shipment
{
[Name(ReportConstants.IssueColumnName)]
public string Issue { get; set; }
[Name(ReportConstants.MaterialReleaseReceiptColumnName)]
public string MaterialReleaseReceipt { get; set; }
[Name(ReportConstants.FreightBillIssueColumnName)]
public string FreightBillIssue { get; set; }
[Name(ReportConstants.InvoiceNumberColumnName)]
public string InvoiceNumber { get; set; }
[Name(ReportConstants.TaxCodeColumnName)]
public string TaxCode { get; set; }
[Name(ReportConstants.ContractorIdColumnName)]
public string ContractorId { get; set; }
[Name(ReportConstants.AddressIdColumnName)]
public string AddressId { get; set; }
[Name(ReportConstants.ContractorNameColumnName)]
public string ContractorName { get; set; }
[Name(ReportConstants.ShipmentCountryColumnName)]
public string ShipmentCountry { get; set; }
public static explicit operator Shipment(ShipmentEntity entity) =>
entity != null
? new Shipment
{
Issue = entity.Issue,
MaterialReleaseReceipt = entity.MaterialReleaseReceipt,
FreightBillIssue = entity.FreightBillIssue,
InvoiceNumber = entity.InvoiceNumber,
TaxCode = entity.TaxCode,
ContractorId = entity.ContractorId,
AddressId = entity.AddressId,
ContractorName = entity.ContractorName,
ShipmentCountry = entity.ShipmentCountry
}
: null;
}
}
The code looks properly, but the behavior of the class is quite strange. In most cases, the generation runs properly, but few times i have noticed a situation, when the MemoryStream object contains no data, even if shipment collection is correct. I believe, such a behavior does not depend on data passed as a parameter. Probably i've made something wrong with the streams. How to use them properly? How to generate csv file correctly using CsvHelper library?
I've found a solution. StreamWriter has to be flushed, after writing records, so now the function looks like:
public async Task<Report> GenerateReportAsync(ICollection<ShipmentEntity> shipmentEntities)
{
var shipment = shipmentEntities
.Select(s => (Shipment) s)
.ToList();
await using var memoryStream = new MemoryStream();
await using var streamWriter = new StreamWriter(memoryStream);
await using var csvWriter = new CsvWriter(streamWriter, CultureInfo.InvariantCulture);
csvWriter.Configuration.Delimiter = ";";
await csvWriter.WriteRecordsAsync(shipment);
await streamWriter.FlushAsync();
var report = new Report
{
Content = memoryStream.ToArray(),
Type = ReportConstants.CsvFileType,
Name = ReportConstants.CsvReportFileName
};
return report;
}
And it works properly :)
I'm having a bit of an issue here. I am getting all my products from a mongodb collection with this function:
public async Task<string> getAllProducts()
{
List<string> all = new List<string>();
var document = await getCollection("produits").Find(new BsonDocument()).ToCursorAsync();
foreach (var doc in document.ToEnumerable())
{
var res = doc.ToJson();
all.Add(res);
}
return JsonConvert.SerializeObject(all);
}
and it returns a JSON that looks like this to my react front end.
{ "_id" : ObjectId("5e49bdf5f040e808847a17d7"),
"email" : "example#gmail.com",
"quantite" : 1,
"matricule" : 1}
problem is i cant parse this in my javascript because of this : ObjectId("5e49bdf5f040e808847a17d7")
Of course I could do some string magic before I parse it, but id rather it be corrected on the server side. So is there a way I can get rid of this problem and get a result like this?
{ "_id" : "5e49bdf5f040e808847a17d7",
"email" : "example#gmail.com",
"quantite" : 1,
"matricule" : 1}
give this a try. it will serialize string ids without objectid stuff.
public static async Task<string> getAllProducts()
{
var collection = db.GetCollection<object>("produits");
var all = new List<object>();
using (var cursor = await collection.FindAsync("{}"))
{
while (await cursor.MoveNextAsync())
{
foreach (var doc in cursor.Current.ToArray())
{
all.Add(doc);
}
}
}
return Newtonsoft.Json.JsonConvert.SerializeObject(all);
}
Fixed by creating a class for the mongodb object.
public class Product
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
public int matricule { get; set; }
public int quantite { get; set; }
public string email { get; set; }
public float prix { get; set; }
public string image { get; set; }
}
get them and deserialize with BsonSerializer :
public async Task<List<Product>> getAllProducts(){
var collection = await getCollection("produits").Find(new BsonDocument()).ToListAsync();
List<Product> all = new List<Product>();
foreach(var doc in collection){
all.Add(BsonSerializer.Deserialize<Product>(doc));
}
return all;
}
return them on request :
[HttpGet]
public async Task<string> ShowProductsAsync()
{
MongodbModel model = new MongodbModel();
var products = await model.getAllProducts();
Console.WriteLine(products);
return JsonConvert.SerializeObject(products);
}
string GetAllProduits(){
var collection = database.GetCollection<BsonDocument>("produits");
var project = Builders<BsonDocument>.Projection.Exclude("_id");
var filter = Builders<BsonDocument>.Filter.Empty;
var rlt=collection.Find(filter).Project(project);
return rlt.ToList().ToJson();
}
This is where I get my data.
$contacts = array();
while ($row = mysqli_fetch_array($stmt))
{
$contact = array("ID" => $row['ProduktID'],
"Name" => $row['ProduktNamn'],
"Number" => $row['ProduktPris']);
array_push($contacts, $contact);
}
echo json_encode(array('results' => $contacts), JSON_PRETTY_PRINT);
And now when I use it in my code in Xamarin I can write the entire thing out in the log but not when I try to write out a certain "Name" of a table for example. This is the code:
static public async Task<JObject> getOurMainInfo()
{
var httpClientRequest = new HttpClient ();
var result = await httpClientRequest.GetAsync ("http://localhost/getmystuff.php");
var resultString = await result.Content.ReadAsStringAsync ();
var jsonResult = JObject.Parse (resultString);
// var jsonResult = JsonConvert.DeserializeObject<Contact>(resultString); //Should I implement this somehow to my JObject? Contact is my public class.
System.Diagnostics.Debug.WriteLine (jsonResult["Name"]); //i get nothing in the log. if i remove ["Name"] i get the entire data in the log.
return jsonResult;
}
My public class (If i need this to connect and work with th edatbase somehow):
public class Contact
{
public string ID { get; set; }
public string Name { get; set; }
public string Number { get; set; }
}
I can't work this one out. If I use NewtonSoft to serialize the object to JSON before a HTTP Post, I receive a 400 from the REST Service. If I just post the JSON as a string, in the below code as "jsonx" it works. However, if I compare the strings "json" and "jsonx" they're the same.
public async Task<String> TransferAsync(String fromAddress, Int64 amount, String toAddress, String assetId)
{
Models.CoinPrism.TransferRequest request = new CoinPrism.TransferRequest()
{
fees = 1000,
from = fromAddress,
};
request.to[0] = new CoinPrism.Transfer()
{
address = toAddress,
amount = amount,
asset_id = assetId
};
String json = Newtonsoft.Json.JsonConvert.SerializeObject(request);
String jsonX = "{ \"fees\": 1000, \"from\": \"1zLkEoZF7Zdoso57h9si5fKxrKopnGSDn\", \"to\": [ { \"address\": \"akSjSW57xhGp86K6JFXXroACfRCw7SPv637\", \"amount\": \"10\", \"asset_id\": \"AHthB6AQHaSS9VffkfMqTKTxVV43Dgst36\" } ]}";
Uri baseAddress = new Uri("https://api.coinprism.com/");
using (var httpClient = new HttpClient { BaseAddress = baseAddress })
{
using (var content = new StringContent(jsonX, System.Text.Encoding.Default, "application/json"))
{
using (var response = await httpClient.PostAsync("v1/sendasset?format=json", content))
{
string responseData = await response.Content.ReadAsStringAsync();
return responseData;
}
}
}
}
Models
public class TransferRequest
{
public Int64 fees { get; set; }
public String from { get; set; }
public Transfer[] to { get; set; }
public TransferRequest(Int32 n = 1)
{
this.to = new Transfer[n];
}
public TransferRequest(Transfer transfer)
{
this.to = new Transfer[1];
this.to[0] = transfer;
}
}
public class Transfer
{
public String address { get; set; }
public Int64 amount { get; set; }
public String asset_id { get; set; }
}
The reason of the error is that they are identical strings but with different encoding.
You are sending the wrong encoding for the string.
1- After serializing, convert the string to ANSI encoding (for example)
2- using (var content = new StringContent(jsonX, System.Text.Encoding.ANSI, "application/json)