Writing JSON file in Xamarin PCL - json

As System.IO.File is not available in Xamarin PCL, I have heard that the only way out of the problem of writing JSON file is to use Streams. However, I haven't found a good link as in how to use them easily. Moreover, is this the only way out or is there anyother method available that can help me in writing output in a JSON format.

As #Jason says, you can use PclStorage.
Otherwise you can use DependencyService and write your code in Platform specific projects.
You can take a look to this repo
TestReadFile
This is something for Android
[assembly: Xamarin.Forms.Dependency(typeof(FilesImplementation))]
namespace TestReadFile.Droid
{
public class FilesImplementation : IFiles
{
public FilesImplementation()
{
}
public string ReadTextFile(string path, string fileName)
{
//throw new NotImplementedException();
using (System.IO.StreamReader sr = new System.IO.StreamReader(System.IO.Path.Combine(path, fileName))){
string line = sr.ReadToEnd();
sr.Close();
return line;
}
}
private string creaFileName(string directory, string fileName) {
string path = RootDirectory();
string file = System.IO.Path.Combine(path, fileName);
return file;
}
public void WriteTextFile(string path, string fileName, string stringToWrite)
{
using (System.IO.StreamWriter sw = new System.IO.StreamWriter(System.IO.Path.Combine(path, fileName),false))
{
sw.WriteLine(stringToWrite);
sw.Close();
}
}
public string RootDirectory()
{
File path = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDcim);
return path.AbsolutePath;
}
}
}
And this is PCL interface
public interface IFiles
{
string ReadTextFile(string path, string fileName);
void WriteTextFile(string path, string filename, string stringToWrite);
string RootDirectory();
}

Related

Reading JSON values from web browser?

I have a random JSON generated online and I am able to print all the values. But how do I read each array separately? For example, the below JSON contains different attributes, how do I read the string name that is an array containing 4 values.
JSON reader:
public class JsonHelper
{
public static T[] getJsonArray<T>(string json)
{
string newJson = "{ \"array\": " + json + "}";
Wrapper<T> wrapper = JsonUtility.FromJson<Wrapper<T>>(newJson);
return wrapper.array;
}
[System.Serializable]
private class Wrapper<T>
{
public T[] array;
}
}
[System.Serializable]
public class RootObject
{
public string name;
public string height;
public string mass ;
}
The below script is used to access the JSON online through RESTApi GET service. I am able to receive the whole text but how I read one single value of name or height or mass?
Script:
using UnityEngine.Networking;
using System.Linq;
using System.Linq.Expressions;
using UnityEngine.UI;
using System.IO;
public class GetData : MonoBehaviour {
// Use this for initialization
void Start () {
StartCoroutine(GetNames());
}
IEnumerator GetNames()
{
string GetNameURL = "https://swapi.co/api/people/1/?format=json";
using(UnityWebRequest www = UnityWebRequest.Get(GetNameURL))
{
// www.chunkedTransfer = false;
yield return www.Send();
if(www.isNetworkError || www.isHttpError)
{
Debug.Log(www.error);
}
else
{
if(www.isDone)
{
string jsonResult = System.Text.Encoding.UTF8.GetString(www.downloadHandler.data);
Debug.Log(jsonResult); //I am getting the result here
}
}
}
}
}
Your API call to 'https://swapi.co/api/people/1/?format=json' returns a single object, not an array.
So after you get your json, you can access name and height etc like:
if (www.isDone)
{
string jsonResult = System.Text.Encoding.UTF8.GetString(www.downloadHandler.data);
Debug.Log(jsonResult); //I am getting the result here
RootObject person = JsonUtility.FromJson<RootObject>(jsonResult);
// then you can access each property
Debug.Log(person.name);
Debug.Log(person.height);
}

Non-blocking parsing of a JSON String to a JsonNode

I'm exploring reactive programming with Spring Webflux and therefore, I'm trying to make my code completely nonblocking to get all the benefits of a reactive application.
Currently my code for the method to parse a Json String to a JsonNode to get specific values (in this case the elementId) looks like this:
public Mono<String> readElementIdFromJsonString(String jsonString){
final JsonNode jsonNode;
try {
jsonNode = MAPPER.readTree(jsonString);
} catch (IOException e) {
return Mono.error(e);
}
final String elementId = jsonNode.get("elementId").asText();
return Mono.just(elementId);
}
However, IntelliJ notifies me that I'm using an inappropriate blocking method call with this code:
MAPPER.readTree(jsonString);
How can I implement this code in a nonblocking way? I have seen that since Jackson 2.9+, it is possible to parse a Json String in a nonblocking async way, but I don't know how to use that API and I couldn't find an example how to do it correctly.
I am not sure why it is saying it is a blocking call since Jackson is non blocking as far as I know. Anyway one way to resolve this issue is to use schedulers if you do not want to use any other library. Like this.
public Mono<String> readElementIdFromJsonString(String input) {
return Mono.just(Mapper.readTree(input))
.map(it -> it.get("elementId").asText())
.onErrorResume( it -> Mono.error(it))
.subscribeOn(Schedulers.boundedElastic());
}
Something along that line.
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
import org.springframework.core.ResolvableType;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.DefaultDataBuffer;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.codec.json.AbstractJackson2Decoder;
import org.springframework.util.MimeType;
import org.springframework.util.MimeTypeUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
#FunctionalInterface
public interface MessageParser<T> {
Mono<T> parse(String message);
}
public class JsonNodeParser extends AbstractJackson2Decoder implements MessageParser<JsonNode> {
private static final MimeType MIME_TYPE = MimeTypeUtils.APPLICATION_JSON;
private static final ObjectMapper OBJECT_MAPPER = allocateDefaultObjectMapper();
private final DefaultDataBufferFactory factory;
private final ResolvableType resolvableType;
public JsonNodeParser(final Environment env) {
super(OBJECT_MAPPER, MIME_TYPE);
this.factory = new DefaultDataBufferFactory();
this.resolvableType = ResolvableType.forClass(JsonNode.class);
this.setMaxInMemorySize(100000); // 1MB
canDecodeJsonNode();
}
#Override
public Mono<JsonNode> parse(final String message) {
final byte[] bytes = message.getBytes(StandardCharsets.UTF_8);
return decode(bytes);
}
private Mono<JsonNode> decode(final byte[] bytes) {
final DefaultDataBuffer defaultDataBuffer = this.factory.wrap(bytes);
return this.decodeToMono(Mono.just(defaultDataBuffer), this.resolvableType, MIME_TYPE, Map.of())
.ofType(JsonNode.class)
.subscribeOn(Schedulers.boundedElastic())
.doFinally((t) -> DataBufferUtils.release(defaultDataBuffer));
}
private void canDecodeJsonNode() {
if (!canDecode(this.resolvableType, MIME_TYPE)) {
throw new IllegalStateException(String.format("JsonNodeParser doesn't supports the given tar`enter code here`get " +
"element type [%s] and the MIME type [%s]", this.resolvableType, MIME_TYPE));
}
}
}

Loading json into my unit test from a text file

I am working in AEM trying to get create txt files with JSON output so that I can load them into my unit test as strings and test my model / model processors. So far I have this...
public String readFile(String path, Charset encoding) throws IOException
{
byte[] encoded = Files.readAllBytes(Paths.get(path));
return new String(encoded, encoding);
}
private String sampleInput = readFile("/test/resources/map/sample-
input.txt",Charset.forName("UTF-8"));
I need sampleInput to take the json that is in 'sampleInput.txt' and convert it to a string. I am also running into issues with the Charset encoding.
I think the easiest way to manage JSON documents you use for unit testing is by keeping them organized in the classpath. Guava provides a neat wrapper for loading classpath resources.
import com.google.common.base.Charsets;
import com.google.common.io.Resources;
import java.io.IOException;
import java.net.URL;
public class TestJsonDocumentLoader {
public TestJsonDocumentLoader(Class clazz) {
this.clazz = clazz;
}
public String loadTestJson(String fileName) {
URL url = Resources.getResource(clazz, fileName);
try {
String data = Resources.toString(url, Charsets.UTF_8);
return data;
} catch (IOException e) {
throw new RuntimeException("Couldn't load a JSON file.", e);
}
}
}
This can then be used to load arbitrary JSON files placed in the same package as the test class. It is assumed that the files are UTF-8 encoded. I suggest keeping all sources encoded that way, regardless of the OS your team is using. It saves you a lot of trouble with version control.
Let's say you have MyTest in src/test/java/com/example/mytestsuite, then you could place a file data.json in src/test/resources/com/example/mytestsuite and load id by calling
TestJsonDocumentLoader loader = new TestJsonDocumentLoader(MyTest.class);
String jsonData = loader.loadTestJson("data.json");
String someOtherExample = loader.loadTestJson("other.json");
Actually, this could be used for all sorts of text files.
You could have also used object mapper from Jackson as an alternative
public class JsonResourceObjectMapper<T> {
private Class<T> model;
public JsonResourceObjectMapper(Class<T> model) {
this.model = model;
}
public T loadTestJson(String fileName) throws IOException{
ClassLoader classLoader = this.getClass().getClassLoader();
InputStream inputStream= classLoader.getResourceAsStream(fileName);
return new ObjectMapper().readValue(inputStream, this.model);
}
}
And then setup a fixture in the test passing a .class
private JsonClass json;
#Before
public void setUp() throws IOException {
JsonResourceObjectMapper mapper = new JsonResourceObjectMapper(JsonClass.class);
json = (JsonClass) mapper.loadTestJson("json/testJson.json");
}
Note that the testJson.json file is in resources/json folder same as what #toniedzwiedz mentioned
So then you could use the json model as:
#Test
public void testJsonNameProperty(){
//act
String name = json.getName();
// assert
assertEquals("testName", name);
}

Azure WebJob- QueueTrigger Staging and Production

I have an Azure WebJob that will be used for Staging and Production. The keys for the proper environments are set in the app.config file.
A ConfigManager class reads the properties from the app.config file.
public static string FirstQueue
{
get { return ConfigurationManager.AppSettings["FirstQueue"]; }
}
I would like the QueueTrigger to read the from the proper queue specified in the app.config file.
public static void ProcessFirstQueue([QueueTrigger(ConfigManager.FirstQueue)] string message)
{
//some function
}
However, the QueueTrigger doesn't seem to like this. I get "An attribute argument must be a constant expression, type of expression...." Any suggestions to set this up. I do not want to hard code values in the queue trigger.
Thanks
You can resolve the queue name at runtime using the
INameResolver
Here's an example to demonstrate this:
WebJob Code:
public class Program
{
private static void Main(string[] args)
{
var config =
new JobHostConfiguration("insert connection string")
{
NameResolver = new QueueNameResolver()
};
var host = new JobHost(config);
host.RunAndBlock();
}
public static void ProcessNotification([QueueTrigger("%queueKey%")] string item)
{
//Handle message
}
public static void ProcessPoison([QueueTrigger("%queueKeyPoison%")] string item)
{
//Handle poison message
}
}
Here's the QueueNameResolver class:
public class QueueNameResolver : INameResolver
{
//name = queueKey or queueKeyPoison
public string Resolve(string name)
{
//Use the name to get it from the app.config or somewhere else.
}
}
Hope this helps,

Remove namespace prefix while JAXB marshalling

I have JAXB objects created from a schema. While marshalling, the xml elements are getting annotated with ns2. I have tried all the options that exist over the net for this problem, but none of them works. I cannot modify my schema or change package-info.java. Please help
After much research and tinkering I have finally managed to achieve a solution to this problem. Please accept my apologies for not posting links to the original references - there are many and I wasn't taking notes - but this one was certainly useful.
My solution uses a filtering XMLStreamWriter which applies an empty namespace context.
public class NoNamesWriter extends DelegatingXMLStreamWriter {
private static final NamespaceContext emptyNamespaceContext = new NamespaceContext() {
#Override
public String getNamespaceURI(String prefix) {
return "";
}
#Override
public String getPrefix(String namespaceURI) {
return "";
}
#Override
public Iterator getPrefixes(String namespaceURI) {
return null;
}
};
public static XMLStreamWriter filter(Writer writer) throws XMLStreamException {
return new NoNamesWriter(XMLOutputFactory.newInstance().createXMLStreamWriter(writer));
}
public NoNamesWriter(XMLStreamWriter writer) {
super(writer);
}
#Override
public NamespaceContext getNamespaceContext() {
return emptyNamespaceContext;
}
}
You can find a DelegatingXMLStreamWriter here.
You can then filter the marshalling xml with:
// Filter the output to remove namespaces.
m.marshal(it, NoNamesWriter.filter(writer));
I am sure there are more efficient mechanisms but I know this one works.
For me, only changing the package-info.java class worked like a charm, exactly as zatziky stated :
package-info.java
#javax.xml.bind.annotation.XmlSchema
(namespace = "http://example.com",
xmlns = {#XmlNs(prefix = "", namespaceURI = "http://example.com")},
elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package my.package;
import javax.xml.bind.annotation.XmlNs;
You can let the namespaces be written only once. You will need a proxy class of the XMLStreamWriter and a package-info.java. Then you will do in your code:
StringWriter stringWriter = new StringWriter();
XMLStreamWriter writer = new Wrapper((XMLStreamWriter) XMLOutputFactory
.newInstance().createXMLStreamWriter(stringWriter));
JAXBContext jaxbContext = JAXBContext.newInstance(Collection.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
jaxbMarshaller.marshal(books, writer);
System.out.println(stringWriter.toString());
Proxy class (the important method is "writeNamespace"):
class WrapperXMLStreamWriter implements XMLStreamWriter {
private final XMLStreamWriter writer;
public WrapperXMLStreamWriter(XMLStreamWriter writer) {
this.writer = writer;
}
//keeps track of what namespaces were used so that not to
//write them more than once
private List<String> namespaces = new ArrayList<String>();
public void init(){
namespaces.clear();
}
public void writeStartElement(String localName) throws XMLStreamException {
init();
writer.writeStartElement(localName);
}
public void writeStartElement(String namespaceURI, String localName) throws XMLStreamException {
init();
writer.writeStartElement(namespaceURI, localName);
}
public void writeStartElement(String prefix, String localName, String namespaceURI) throws XMLStreamException {
init();
writer.writeStartElement(prefix, localName, namespaceURI);
}
public void writeNamespace(String prefix, String namespaceURI) throws XMLStreamException {
if(namespaces.contains(namespaceURI)){
return;
}
namespaces.add(namespaceURI);
writer.writeNamespace(prefix, namespaceURI);
}
// .. other delegation method, always the same pattern: writer.method() ...
}
package-info.java:
#XmlSchema(elementFormDefault=XmlNsForm.QUALIFIED, attributeFormDefault=XmlNsForm.UNQUALIFIED ,
xmlns = {
#XmlNs(namespaceURI = "http://www.w3.org/2001/XMLSchema-instance", prefix = "xsi")})
package your.package;
import javax.xml.bind.annotation.XmlNs;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
You can use the NamespacePrefixMapper extension to control the namespace prefixes for your use case. The same extension is supported by both the JAXB reference implementation and EclipseLink JAXB (MOXy).
http://wiki.eclipse.org/EclipseLink/Release/2.4.0/JAXB_RI_Extensions/Namespace_Prefix_Mapper
Every solution requires complex overwriting or annotations which seems not to work with recent version. I use a simpler approach, just by replacing the annoying namespaces. I wish Google & Co would use JSON and get rid of XML.
kml.marshal(file);
String kmlContent = FileUtils.readFileToString(file, "UTF-8");
kmlContent = kmlContent.replaceAll("ns2:","").replace("<kml xmlns:ns2=\"http://www.opengis.net/kml/2.2\" xmlns:ns3=\"http://www.w3.org/2005/Atom\" xmlns:ns4=\"urn:oasis:names:tc:ciq:xsdschema:xAL:2.0\" xmlns:ns5=\"http://www.google.com/kml/ext/2.2\">", "<kml>");
FileUtils.write(file, kmlContent, "UTF-8");