Easy, asynchronous, annotation-based SOAP for Android

Related tags

Networking IceSoap
Overview

IceSoap

IceSoap provides quick, easy, asynchronous access to SOAP web services from Android devices. It allows for SOAP responses to be bound to Java POJOs via annotations (using a subset of xpath), while still retaining the speed of the native android XmlPullParser.

E.g. if I wanted to get data from a SOAP service that returned an envelope like this:

<Dictionary>
    <Id>1</Id>
    <Name>Blah</Name>
</Dictionary>

I'd write code like this to parse it:

@XMLObject("//Dictionary")
public class Dictionary {
    @XMLField("Id")
    private String id;

    @XMLField("Name")
    private String name;
}

IceSoap works more like Jackson than JAX-WS. You simply create a Java POJO to represent the object you want to get from the web service, annotate it with XPaths so it can find the fields it needs, then let IceSoap parse it for you. Features like the use of background threads and streamed parsing is baked into the code, so all you have to worry about is what to send and what to do with it when it comes back.

So download IceSoap, read the Getting Started Guide, have a look at the example and get going! Javadoc is here.

Github vs Google Code

This used to be hosted on Google Code until Google did their thing and dumped it like an unwanted Christmas puppy. As a result the old issues that have been raised since the first release aren't actually on this repo, instead they're on the one that got converted here: https://github.com/AlexGilleran/icesoap-gcode-import/issues.

Copyright 2012-2016 Alex Gilleran

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Comments
  • Error:warning: Ignoring InnerClasses attribute for an anonymous inner class

    Error:warning: Ignoring InnerClasses attribute for an anonymous inner class

    Hi! I have problem use gradle plugin 2.0 and multidex How can I fix this?

    Error:warning: Ignoring InnerClasses attribute for an anonymous inner class Error:(org.jaxen.dom.DocumentNavigator$6) that doesn't come with an Error:associated EnclosingMethod attribute. This class was probably produced by a Error:compiler that did not target the modern .class file format. The recommended Error:solution is to recompile the class from source, using an up-to-date compiler Error:and without specifying any "-target" type options. The consequence of ignoring Error:this warning is that reflective operations on this class will incorrectly Error:indicate that it is not an inner class. Error:warning: Ignoring InnerClasses attribute for an anonymous inner class Error:(org.jaxen.dom.DocumentNavigator$1) that doesn't come with an Error:associated EnclosingMethod attribute. This class was probably produced by a Error:compiler that did not target the modern .class file format. The recommended Error:solution is to recompile the class from source, using an up-to-date compiler Error:and without specifying any "-target" type options. The consequence of ignoring Error:this warning is that reflective operations on this class will incorrectly Error:indicate that it is not an inner class.

    opened by ulx 11
  • setDebugMode(true) not working

    setDebugMode(true) not working

    I created a request and used request.setDebugMode(true) to activate debug mode. But when I called Log.i("request", request.getRequestXML()) a NullPointerException occurred that says "println needs a message", which means the getRequestXML() returned null. It used to work, but since some time the debug mode seems not to be working any more. How could I solve this problem?

    opened by beta 7
  • How to create SOAP request with CDATA

    How to create SOAP request with CDATA

    Hi,

    Is there any way to send eg. html code in CDATA tag? When I add text node like this: <![CDATA[ <Request> <Authentication CMId="68" Function="1" Guid="5594FB83-F4D4-431F-B3C5-EA6D7A8BA795" Password="poihg321TR"/> <Establishment Id="4297867"/> </Request> ]]>

    The problem is: <![CDATA[ ]]> is displaying like &lt;![CDATA[ and ]]&gt;

    Thanks!

    opened by pzienowicz 5
  • Soap request with out name space.

    Soap request with out name space.

    I'm soap for billing api. WSDL URL : http://94.206.70.106:8181/cxf/services/UnifiedPartner?wsdl My request is :

    <?xml version="1.0" encoding="UTF-8"?>
    <v:Envelope xmlns:v="http://schemas.xmlsoap.org/soap/envelope/" xmlns:c="http://schemas.xmlsoap.org/soap/encoding/" xmlns:d="http://www.w3.org/2001/XMLSchema" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
       <v:Header />
       <v:Body>
          <purchaseConsumeProduct xmlns="https://94.206.70.106:8181/">
           <userId>97XXXXXXX</userId>
             <serviceId>S-KXXXXX</serviceId>
             <premiumResourceType>MP-xxx-xx-xxxx-Sub-B2-D-xx</premiumResourceType>
             <productId>Daily MAP Test Sub V2 IN</productId>
             <purchaseMetas>
                <key>du:assetDescription</key>    
                <value>Subscription</value>
             </purchaseMetas>
             <!--Zero or more repetitions:-->
             <billingMetas>
                <key>du:assetID</key>
                <value>-</value>
             </billingMetas>
               <billingMetas>
                <key>du:contentType</key>
                <value>mobileApp</value>
             </billingMetas>
             <billingMetas>
                <key>du:channel</key>
                <value>COMMERCE_API</value>
             </billingMetas>
             <!--Zero or more repetitions:-->
             <usageMetas>
                <key>du:externalid</key>
                <value>S-XXXXXX</value>
             </usageMetas>
          </purchaseConsumeProduct>
       </v:Body>
    </v:Envelope>
    

    But they need like below

    <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ws="http://ws.api.sdp.ericsson.com/">
       <soapenv:Header/>
       <soapenv:Body>
          <ws:purchaseConsumeProduct>//Method name
             <userId>97XXXXXXX</userId>
             <serviceId>S-KXXXXX</serviceId>
             <premiumResourceType>MP-xxx-xx-xxxx-Sub-B2-D-xx</premiumResourceType>
             <productId>Daily MAP Test Sub V2 IN</productId>
             <purchaseMetas>
                <key>du:assetDescription</key>    
                <value>Subscription</value>
             </purchaseMetas>
             <!--Zero or more repetitions:-->
             <billingMetas>
                <key>du:assetID</key>
                <value>-</value>
             </billingMetas>
               <billingMetas>
                <key>du:contentType</key>
                <value>mobileApp</value>
             </billingMetas>
             <billingMetas>
                <key>du:channel</key>
                <value>COMMERCE_API</value>
             </billingMetas>
             <!--Zero or more repetitions:-->
             <usageMetas>
                <key>du:externalid</key>
                <value>S-XXXXXX</value>
             </usageMetas>
          </ws:purchaseConsumeProduct>
       </soapenv:Body>
    </soapenv:Envelope>
    
    

    How i can achieve above request with IceSoap. It's possible with IceSoap?

    Please help me here.

    opened by moonandroid 4
  • Run time Error gradle minifyEnabled true

    Run time Error gradle minifyEnabled true

    Hi mate,

    Thanks a lot for the lib, is great and pretty simple. I love it. But I am finding a problem that i am not sure how to solve.

    When I build my app with gradle "minifyEnabled true" it's fine, but when I use your lib, the app freezes in runtime when I call in the code

    requestFactory.buildListRequest from the class RequestFactoryImpl

    When working with retrofit or many other libraries, their documentation comes with information on how to set up the proguard-rules file with their lib.

    Do you have any info on this? If I build my app with minifyEnabled true and debuggable true, but of course, no obfuscation is taking place

    opened by unxavi 4
  • Access to attributes

    Access to attributes

    Not sure if this is not supported or just me being stupid but basically I have this SOAP response:

    <SOAP-ENV:Envelope 
        xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope" 
        xmlns:trt="http://www.onvif.org/ver10/media/wsdl" 
        xmlns:tt="http://www.onvif.org/ver10/schema">
       <SOAP-ENV:Body>
          <trt:GetProfilesResponse>
             <trt:Profiles fixed="true" token="000">
                <tt:Name>Profile_000</tt:Name>
                <tt:VideoSourceConfiguration token="000">
                   <tt:Name>VideoS_000</tt:Name>
        ....
    </SOAP-ENV:Envelope>
    

    I need to access the value of the attribute "token" in the "Profiles" node, is it supported by the parser in any way or do I have to parse the xml manually? Obviously the XMLField annotation does not work for this.

    Thanks in advance

    opened by danielnilsson9 3
  • HTTP Connection

    HTTP Connection

    Hi Alex,

    Thanks for your very useful library. Currently, Android HTTP Connection using HttpClient/HttpPost have been deprecated. Do you have plan for updating it?

    I tried to use Retrofit but I cannot send a String or SOAPEnvelope object to server. Server doesn't know it is a soap Object

    enhancement 
    opened by nguyenhuutinh 3
  • How to update DEFAULT_SOCKET_TIMEOUT for any request?

    How to update DEFAULT_SOCKET_TIMEOUT for any request?

    Hi, Is there a way we can update the timeout duration for any request? I have looked around and located the variable DEFAULT_SOCKET_TIMEOUT in the HTTPDefaults class. But I couldn't find any way I can update the value stored in this variable. Please let me know if it is possible to change the timeout with the current version.

    P.S. thanks for this great library, it really has been very helpful.

    opened by qdupendra 2
  • Out of memory

    Out of memory

    Hi, I have a memory problem in my test application. This application contains only the ice soap lib and the sense behind this application is, to load a zip over soap and save the zip into the cache. If I request the file over soap I got a out of memory exception during the parser.

    at java.lang.AbstractStringBuilder.enlargeBuffer(AbstractStringBuilder.java:~94) 04-16 15:06:12.121 699 724 I dalvikvm: at java.lang.AbstractStringBuilder.append0(AbstractStringBuilder.java:124) 04-16 15:06:12.121 699 724 I dalvikvm: at java.lang.StringBuilder.append(StringBuilder.java:271) 04-16 15:06:12.121 699 724 I dalvikvm: at org.kxml2.io.KXmlParser.readValue(KXmlParser.java:1338) 04-16 15:06:12.121 699 724 I dalvikvm: at org.kxml2.io.KXmlParser.next(KXmlParser.java:390) 04-16 15:06:12.121 699 724 I dalvikvm: at org.kxml2.io.KXmlParser.next(KXmlParser.java:310) 04-16 15:06:12.121 699 724 I dalvikvm: at com.alexgilleran.icesoap.parser.impl.XPathPullParserImpl.next(XPathPullParserImpl.java:90) 04-16 15:06:12.121 699 724 I dalvikvm: at com.alexgilleran.icesoap.parser.impl.BaseIceSoapParserImpl.parse(BaseIceSoapParserImpl.java:175) 04-16 15:06:12.121 699 724 I dalvikvm: at com.alexgilleran.icesoap.parser.impl.BaseIceSoapParserImpl.parse(BaseIceSoapParserImpl.java:127) 04-16 15:06:12.121 699 724 I dalvikvm: at com.alexgilleran.icesoap.parser.impl.BaseIceSoapParserImpl.parse(BaseIceSoapParserImpl.java:109) 04-16 15:06:12.121 699 724 I dalvikvm: at com.alexgilleran.icesoap.request.impl.RequestImpl.run(RequestImpl.java:333) 04-16 15:06:12.121 699 724 I dalvikvm: at com.alexgilleran.icesoap.request.impl.RequestImpl.executeBlocking(RequestImpl.java:227)

    My application has only 128MB of memory, is there a way to load the data in a efficient way?

    Best regards LiTe

    opened by LiTe1991 1
  • Child nodes not being correctly picked up

    Child nodes not being correctly picked up

    Apologies for the general question but I wasn't getting much help from StackOverflow.

    I'm trying to parse some XML using IceSoap but it's returning null for some values, I think there's an error with my parse classes.

    This is what my XML looks like:

    <soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
       <soap:Body>
          <GetDepBoardWithDetailsResponse xmlns="http://thalesgroup.com/RTTI/2016-02-16/ldb/">
             <GetStationBoardResult xmlns:lt3="http://thalesgroup.com/RTTI/2015-05-14/ldb/types" xmlns:lt5="http://thalesgroup.com/RTTI/2016-02-16/ldb/types" xmlns:lt4="http://thalesgroup.com/RTTI/2015-11-27/ldb/types" xmlns:lt="http://thalesgroup.com/RTTI/2012-01-13/ldb/types" xmlns:lt2="http://thalesgroup.com/RTTI/2014-02-20/ldb/types">
                <lt4:generatedAt>2017-01-10T21:52:46.8436123+00:00</lt4:generatedAt>
                <lt4:locationName>Birmingham New Street</lt4:locationName>
                <lt4:crs>BHM</lt4:crs>
                <lt4:filterLocationName>Redditch</lt4:filterLocationName>
                <lt4:filtercrs>RDC</lt4:filtercrs>
                <lt4:platformAvailable>true</lt4:platformAvailable>
                <lt5:trainServices>
                   <lt5:service>
                      <lt4:std>22:14</lt4:std>
                      <lt4:etd>On time</lt4:etd>
                      <lt4:platform>9B</lt4:platform>
                      <lt4:operator>London Midland</lt4:operator>
                      <lt4:operatorCode>LM</lt4:operatorCode>
                      <lt4:serviceType>train</lt4:serviceType>
                      <lt4:serviceID>REA+a3xzY/NH5bk/XmEgww==</lt4:serviceID>
                      <lt5:rsid>LM139600</lt5:rsid>
                      <lt5:origin>
                         <lt4:location>
                            <lt4:locationName>Lichfield Trent Valley</lt4:locationName>
                            <lt4:crs>LTV</lt4:crs>
                         </lt4:location>
                      </lt5:origin>
                      <lt5:destination>
                         <lt4:location>
                            <lt4:locationName>Redditch</lt4:locationName>
                            <lt4:crs>RDC</lt4:crs>
                         </lt4:location>
                      </lt5:destination>
                      etc.
    

    Service (parent class):

    @XMLObject("//GetStationBoardResult")
    public class Service
    {
        @XMLField("trainServices")
        private TrainServices trainServices;
    
        @XMLField("generatedAt")
        private String generatedAt;
    }
    

    TrainServices

    @XMLObject("//trainServices")
    public class TrainServices
    {
        @XMLField("service")
        private InnerService service;
    }
    

    InnerService

    @XMLObject("//service")
    public class InnerService
    {
        @XMLField("std")
        private String std;
    }
    

    My parent class Service gets instantiated and the value for generatedAt does get fetched but anything beyond that does not get included. TrainServices is always null. Where am I going wrong?

    opened by jbmlaird 1
  • executeBlocking() not working

    executeBlocking() not working

    I created a request and i used .executeBlocking() But a NetworkOnMainThreadException occurred that says android.os.NetworkOnMainThreadException at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1147). How could I solve this problem?

    opened by adnen-chouibi 1
  • Proguard issue with xpath

    Proguard issue with xpath

    have a issue with an Android app: when I compile with minifyEnabled true I guess it's an issue with the obfuscation, I have this error:

     b.b.a.c.a: The xpath expression //LoginServicesResponse specified for e was an invalid XPath expression 
    ...
    ...
    Caused by: java.lang.ClassNotFoundException: Didn't find class "org.jaxen.saxpath.base.XPathReader" on path: DexPathList[[zip file "/data/app/xxx.xxxx.xxxx-VosorY5JNoJmDt20C6tGgg==/base.apk"],nativeLibraryDirectories=[/data/app/re.touchwa.braccointernational-VosorY5JNoJmDt20C6tGgg==/lib/arm64, /system/lib64, /system/product/lib64]]
    

    My class with //LoginServicesResponse start with:

    @XMLObject("//LoginServicesResponse")
    public class LoginServicesResponse {
        @XMLField("LoginServicesResult")
        private String result;
       ...
    

    I'd add the right rule on Proguard file, I've tried with:

    I'd add the right rule on Proguard file, I've tried with:

    -keepnames class org.jaxen.saxpath.base.XPathReader or

    -keep class org.jaxen.saxpath.base.XPathReader or

    -keep classorg.jaxen.saxpath.base.XPathReader.** { *; } but they don't solve the problem.

    What am I doing wrong?

    opened by EnricoAncis 2
  • Detect if request was started

    Detect if request was started

    Hi, I need to know if request was started because I don't receive any response from the server. So I don't know if the issue is in my app or on the server side.

    Any idea how to achieve it?

    request.execute(); is called but nothing is happening later. Neither onCompletion nor onException from observer is fired.

    Ahh, I have no access to server logs, but the admin says that request is not reaching the server.

    opened by pzienowicz 1
  • Custom processor on tag not working

    Custom processor on tag not working

    When trying to use a custom processor for a ParentNode (not a text node) it doesn't work: the related fiels stay null. Here is the code for the matching class:

    @XMLObject("//RequestSecurityTokenResponse")
    public class TokenWrapper {
        
        public static class RequestedSecurityToken {
            private String xml;
            public RequestedSecurityToken(String lxml) {
                xml=lxml;
            }
            public String getXml() {
                return xml;
            }
        }
    
        @XMLField(value="RequestedSecurityToken",processor=RequestedSecurityTokenProcessor.class)
        private RequestedSecurityToken requestedSecurityToken;
    
        public String getToken() {
            return requestedSecurityToken.getXml();
        }
    
        public static class RequestedSecurityTokenProcessor implements Processor<RequestedSecurityToken> {
            @Override
            public RequestedSecurityToken process(String inputValue) {
                return new RequestedSecurityToken(inputValue);
            }
        }
    }
    

    I did look at the code and I think that the bug is in the IseSoapParserImpl at line 401 in the needsParser function: should return true is hasProcessor is true:

    	private boolean needsParser(Field fieldToSet) {
    		// Is it a text node?
    		if (parserMap.containsKey(fieldToSet.getType())) {
    			return false;
    		}
    
    		// Does it have a processor to deal with it rather than a generic
    		// parser?
    		if (hasProcessor(fieldToSet)) {
                   //Should return true I think
                   //or else the cond should be !hasProcessor(fieldToSet)
    			return false;
                  }
    
    		// Is it a list of text nodes?
    		if (List.class.isAssignableFrom(fieldToSet.getType())
    				&& parserMap.containsKey(getListItemClass(fieldToSet.getGenericType()))) {
    			return false;
    		}
    
    		// No to all of the above, .'. it needs a parser
    		return true;
    	}
    
    opened by xlaussel 3
  • Session cookies managment

    Session cookies managment

    Someone can help me Please, How to save and return cookies to the web service? i need to retrieve header values from the web service Authetification response. I would like to save any returned cookies in my shared preferences and return them in request header for the rest of services

    opened by adnen-chouibi 1
  • IncompatibleClassChangeError on Android Lollipop

    IncompatibleClassChangeError on Android Lollipop

    In Crashlytics I see a lot of crashes which all come down to the same thing:

    Fatal Exception: java.lang.IncompatibleClassChangeError Couldn't find com.alexgilleran.icesoap.annotation.XMLField.value libcore.reflect.AnnotationAccess.toAnnotationInstance (AnnotationAccess.java:659) java.lang.reflect.Field.getAnnotation (Field.java:242)

    For example at lines com.alexgilleran.icesoap.parser.impl.IceSoapParserImpl.onText (IceSoapParserImpl.java:348) and com.alexgilleran.icesoap.parser.impl.IceSoapParserImpl.hasProcessor (IceSoapParserImpl.java:381)

    These crashes all happen on Samsung devices with Android 5. I'm using version 1.0.7 of this library.

    Is there any way this can be fixed?

    Thanks in advance.

    opened by inneke-dc 3
Owner
Alex Gilleran
Developerer at @siteminder-au
Alex Gilleran
Asynchronous socket, http(s) (client+server) and websocket library for android. Based on nio, not threads.

AndroidAsync AndroidAsync is a low level network protocol library. If you are looking for an easy to use, higher level, Android aware, http request li

Koushik Dutta 7.3k Jan 2, 2023
Android Asynchronous Networking and Image Loading

Android Asynchronous Networking and Image Loading Download Maven Git Features Kotlin coroutine/suspend support Asynchronously download: Images into Im

Koushik Dutta 6.3k Dec 27, 2022
An android asynchronous http client built on top of HttpURLConnection.

Versions 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 Version 1.0.6 Description An android asynchronous http client based on HttpURLConnection. Updates U

David 15 Mar 29, 2020
Asynchronous Http and WebSocket Client library for Java

Async Http Client Follow @AsyncHttpClient on Twitter. The AsyncHttpClient (AHC) library allows Java applications to easily execute HTTP requests and a

AsyncHttpClient 6k Jan 8, 2023
Write your asynchronous Network / IO call painlessly in Kotlin !!

Asynkio : Write asynced IO/ Network calls painlessly on android | | | Documentation Write your network requests, IO calls in android with Kotlin seaml

Nikhil Chaudhari 82 Jan 26, 2022
Android Easy Http - Simplest android http request library.

Android Easy Http Library 繁體中文文檔 About Android Easy Http Library Made on OkHttp. Easy to do http request, just make request and listen for the respons

null 13 Sep 30, 2022
IceNet - Fast, Simple and Easy Networking for Android

IceNet FAST, SIMPLE, EASY This library is an Android networking wrapper consisting of a combination of Volley, OkHttp and Gson. For more information s

Anton Nurdin Tuhadiansyah 61 Jun 24, 2022
Compact and easy to use, 'all-in-one' android network solution

Deprecated Unfortunately due to many reasons including maintenance cost, this library is deprecated. I recommend to use Retrofit/OkHttp instead. Curre

Orhan Obut 585 Dec 30, 2022
Android 网络请求框架,简单易用,so easy

简单易用的网络框架 项目地址:Github、码云 博客地址:网络请求,如斯优雅 点击此处下载Demo 另外对 OkHttp 原理感兴趣的同学推荐你看以下源码分析文章 OkHttp 精讲:拦截器执行原理 OkHttp 精讲:RetryAndFollowUpInterceptor OkHttp 精讲:B

Android轮子哥 939 Dec 28, 2022
Easy-to-use webserver with Markdown.

Easy-to-use webserver with Markdown.

Tim 4 May 21, 2022
Android network client based on Cronet. This library let you easily use QUIC protocol in your Android projects

Android network client based on Cronet. This library let you easily use QUIC protocol in your Android projects

VK.com 104 Dec 12, 2022
Multiplatform coroutine-based HTTP client wrapper for Kotlin

networkinkt This is a lightweight HTTP client for Kotlin. It relies on coroutines on both JS & JVM platforms. Here is a simple GET request: val text =

Egor Zhdan 31 Jul 27, 2022
A gRPC Kotlin based server and client starter that builds with Gradle and runs on the JVM

gRPC Kotlin starter Overview This directory contains a simple bar service written as a Kotlin gRPC example. You can find detailed instructions for bui

Hypto 8 Sep 19, 2022
A Kotlin client for the gRPC based Bar Service

Bar Service Kotlin Client Overview This directory contains a simple bar service client written in Kotlin against generated models (protos) You can fin

Hypto 1 Nov 18, 2021
Cli lightning network server, based on LDK (rust-lightning). Provides DUMB-RPC interface (telnet friendly).

Hello Lightning Cli lightning network server, based on LDK (rust-lightning). Provides DUMB-RPC interface (telnet friendly). Example: Build it run it:

null 9 Mar 28, 2022
A "fluent" OkHTTP library for Kotlin based on string extensions.

okfluent A "fluent" OkHTTP library for Kotlin based on string extensions. Do not take this project seriously, I just wanted to show that this kind of

Duale Siad 0 Nov 23, 2021
:satellite: [Android Library] Simplified async networking in android

Android library that simplifies networking in android via an async http client. Also featured in [Awesome Android Newsletter #Issue 15 ] Built with ❤︎

Nishant Srivastava 36 May 14, 2022
Square’s meticulous HTTP client for the JVM, Android, and GraalVM.

OkHttp See the project website for documentation and APIs. HTTP is the way modern applications network. It’s how we exchange data & media. Doing HTTP

Square 43.4k Jan 5, 2023
A type-safe HTTP client for Android and the JVM

Retrofit A type-safe HTTP client for Android and Java. For more information please see the website. Download Download the latest JAR or grab from Mave

Square 41k Jan 5, 2023