Același sound ca acum 40 de ani!
Aceleași mișcări ca acum 40 de ani!
Aceleași versuri ca acum 40 de ani!
Pe scurt, imi place!!!
Un nou album.
Pe 13 noiembrie.
Același sound ca acum 40 de ani!
Aceleași mișcări ca acum 40 de ani!
Aceleași versuri ca acum 40 de ani!
Pe scurt, imi place!!!
Un nou album.
Pe 13 noiembrie.
Am observat anumite formate de dată și timp care nu pot fi citite corecte cu api-ul JAXB din Java. Mai bine spus, nu pot fi citite, adică acel câmp mapat în clasa din aplicație este null.
Un exemplu de format de dată și timp cu timezone ar fi acesta 2020-10-13T12:25:00+0200.
In aplicație, campul aferent timestamp-ului are ca tip de data XMLGregorianCalendar
, o clasă destul de veche folosită pentru a reprezenta timestamp-uri. Clasă veche și un format de timp mai deosebit din punct de vedere al timezone-ului nu fac casă bună și când am pornit testul pentru a vedea dacă se poate citi acel fișier, a ieșit o foarte frumoasă exceție NullPointerException.
Soluția cea mai la îndemană a fost să înlocuiesc XMLGregorianCalendar
cu String
și astfel citirea se face corect, eventualele erori, cum ar fi lipsa timestamp-ului sau invaliditatea lui, rămânând de tratat la un nivel mai sus, al aplicației.
O altă soluție ar mai fi screrea unui XmlAdapter custom care poate fi folosit pentru a citi corect acel câmp de dată din fișier.
Un exemplu, în cazul problemei de față ar fi acesta:
package com.mycompany.app; | |
import javax.xml.bind.ValidationEventHandler; | |
import javax.xml.bind.annotation.adapters.XmlAdapter; | |
import javax.xml.datatype.DatatypeFactory; | |
import javax.xml.datatype.XMLGregorianCalendar; | |
import java.text.SimpleDateFormat; | |
import java.util.Date; | |
import java.util.GregorianCalendar; | |
public class DateTimeAdapter extends XmlAdapter<String, XMLGregorianCalendar> { | |
/** | |
* Convert a value type to a bound type. | |
* | |
* @param v The value to be converted. Can be null. | |
* @throws Exception if there's an error during the conversion. The caller is responsible for | |
* reporting the error to the user through {@link ValidationEventHandler}. | |
*/ | |
@Override | |
public XMLGregorianCalendar unmarshal(String v) throws Exception { | |
Date date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX").parse(v); | |
GregorianCalendar c = new GregorianCalendar(); | |
c.setTime(date); | |
XMLGregorianCalendar date2 = DatatypeFactory.newInstance().newXMLGregorianCalendar(c); | |
return date2; | |
} | |
/** | |
* Convert a bound type to a value type. | |
* | |
* @param v The value to be convereted. Can be null. | |
* @throws Exception if there's an error during the conversion. The caller is responsible for | |
* reporting the error to the user through {@link ValidationEventHandler}. | |
*/ | |
@Override | |
public String marshal(XMLGregorianCalendar v) throws Exception { | |
return null; | |
} | |
} |
Și se folosește ca adnotare pe campul respectiv din clasă
@XmlJavaTypeAdapter(DateTimeAdapter.class) protected XMLGregorianCalendar timestamp;
Drumul către Iad sau traficul?
Se pare ca doar traficul.
O zi de 20 septembrie și o cana de cafea. Și un blogger fără idee de articol!
Cam asta s-a întâmplat în cei 11 ani. Scurt și la obiect!
La restul de analize, lăsăm profesioniștii să discute 😊
Sarbatorim deschiderea magistralei M5 Drumul Taberei from Romania
Deși aveam în plan să scriu despre ele, dar având în vedere că au trecut vreo 4 luni și ceva decând au apărut și nu am ce să aduc nou, m-am gândit să vad cum se descurcă cu una din cele mai comune lucruri într-o aplicație, deserializarea unui răspuns json.
Ca API vom folosi unul dummy care va returna o lista de ToDo-uri cu true sau false, în funcție dacă au fost efectuate sau nu și le vom agrega după acestă condiție.
Primul lucru este să creăm un record care să aibă câmpurile json-ului din răspuns.
{ "userId": 1, "id": 1, "title": "delectus aut autem", "completed": false }
Deci record-ul va avea un userId și un id de tip int, precum un câmp title de tip string si un completed boolean.
record Todo( @JsonProperty("userId") int userId, @JsonProperty("id") int id, @JsonProperty("title") String title, @JsonProperty("completed") boolean completed) {}
Întrucât folosim Jackson pentru deserializare și record-urile fiind o funcționalitate în preview, va fi nevoie să specificam că acele câmpuri sunt chiar cele din json. Fără ele, Jackosn va arunca o exceptie:
Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidDefinitionException
Cu record-ul creat după strucutura json-ului, este nevoie să facem call-ul la api, folosind clinetul http.
String DATA_URL = "https://jsonplaceholder.typicode.com/todos"; var httpClient = HttpClient.newHttpClient(); var httpRequest = HttpRequest.newBuilder() .uri(URI.create(DATA_URL)) .build(); var httpResponse = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString()); var body = httpResponse.body();
Codul este simplu, nu are nimic special. Întrucât este un raspuns de tip text, sunt interesat să îl primesc așa cum este și îl pun într-o variabilă
Urmează etapa de deserializare și agregare
var mapper = new ObjectMapper(); Todo[] todos = mapper.readValue(body, Todo[].class); //because in json representation, there is an array of todos //System.out.println(todos[1].title()); var doneTodos = Arrays.stream(todos) .filter(Todo::completed) .count(); System.out.println("Completed: " + doneTodos); System.out.println("Not completed: " + (todos.length - doneTodos));
Avem un array de todo-uri, lucru pe care trebuie sa il specificam metodei readValue()
, împreună cu ceea ce vrem să deserializăm, în cazul nostru, variabila body, mai precis, conținutul ei.
Urmează o simplă filtrare dupa todo-urile completed. Aici se vede unul din avantajele de a folosi record-uri. Avem acces la propietați, exect ca la clase cu getteri și setteri, dar în cazul record-urilor sunt generate de compilator. Plus altele.
Marele final reprezinta afișarea la consolă 🙂
Ca pe final de articol, adaugarea lor în Java este binevenită și va reduce mult zgomotul de cod. Codul complet:
package com.company; | |
import com.fasterxml.jackson.annotation.JsonProperty; | |
import com.fasterxml.jackson.databind.ObjectMapper; | |
import java.io.IOException; | |
import java.net.URI; | |
import java.net.http.HttpClient; | |
import java.net.http.HttpRequest; | |
import java.net.http.HttpResponse; | |
import java.util.Arrays; | |
public class Main { | |
public static void main(String[] args) throws IOException, InterruptedException { | |
// write your code here | |
String DATA_URL = "https://jsonplaceholder.typicode.com/todos"; | |
var httpClient = HttpClient.newHttpClient(); | |
var httpRequest = HttpRequest.newBuilder() | |
.uri(URI.create(DATA_URL)) | |
.build(); | |
var httpResponse = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString()); | |
var body = httpResponse.body(); | |
var mapper = new ObjectMapper(); | |
Todo[] todos = mapper.readValue(body, Todo[].class); //because in json representation in an array of todos | |
//System.out.println(todos[1].title()); | |
var doneTodos = Arrays.stream(todos) | |
.filter(Todo::completed) | |
.count(); | |
System.out.println("Completed: " + doneTodos); | |
System.out.println("Not completed: " + (todos.length – doneTodos)); | |
} | |
} | |
//https://angiejones.tech/deserializing-api-responses-into-java-records | |
record Todo( | |
@JsonProperty("userId") | |
int userId, | |
@JsonProperty("id") | |
int id, | |
@JsonProperty("title") | |
String title, | |
@JsonProperty("completed") | |
boolean completed) {} |
PS: Vom avea record-uri și in C# la versiunea 9.
Nu pot spune că bucătărisitul este un lucru la care sunt priceput, DAR a ieșit. Am făcut pentru mai multă lume și am primit feedback pozitiv 😁
Bine, nu că ar fi ceva greu de făcut. Eu am folosit pentru rețetă vreo 2-3 linguri de ness, 2-3 linguri de zahăr si tot atâtea de apa fierbinte. Se amestecă totul cu un mixer. Merge și cu unul clasic, dar am văzut ca iese mai bine și este mai ușor de bătut cu ăsta. Spuma rezultată se pune peste o ceașca/cană cu lapte rece sau cald și se amestecă.
Rezultatul(neamestecat), ca mai jos 😀
Ca și concluzie, pot spune că este ceva ce se prepară ușor, nu are nevoie de cine știe ce ustensile și este potrivit pentru acestă perioadă 🙂
Datorită situației mai speciale, conferința s-a ținut online. Printre tot felul de sesiuni, care mai de care, s-au anunțat câteva lucruri interesante.
Pe lânga cele de mai sus, mai sunt multe altele din toate domeniile, machine learning, c++ tooling, dar astea nu pot spune că mă interesează așa mult. Dar nici nu lucrez cu ele.
Oh Microsoft!
What a great times to be alive 🙂
Am început să mă uit la vidoclipurile lor cam acum o săptămână și sunt al naibi de interesante. De la tehnică din cele două războaie mondiale, până la Vestul Sălbatic, Revoluția industrială și altele.
Și sunt scurte videoclipurile. Cele mai lungi cred că au vreo 10-15 minute maxim, dar media este de 5-10 minute, perfecte pentru a fi vizionate într-o scurtă pauză.
La mine plouă :/
Astea fiind zise și fără nicio ordine a preferinței:
https://quip.com/2gwZArKuWk7W – O selecție de query-uri SQL bune pentru analiza de date. Poate pot să apară și pe la interviuri
https://isank.dev/posts/java-streams-real-life-approach/ – embrace it – foreach and if boilerplate killer.
https://www.digitalmars.com/articles/b05.html – redundanță în limbajele de programare. Ceva la nivel teoretic.
https://venam.nixers.net/blog/unix/2020/05/02/time-on-unix.html – un articol foarte lung despre timp in UNIX. La o citire în diagonală pare bun și poate este util pentru cei care scriu cod sensibil la timp.
Cam atât pentru moment.